sequel 4.21.0 → 4.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/README.rdoc +3 -4
  4. data/doc/opening_databases.rdoc +10 -75
  5. data/doc/release_notes/4.22.0.txt +72 -0
  6. data/lib/sequel/adapters/ado/access.rb +1 -1
  7. data/lib/sequel/adapters/cubrid.rb +3 -3
  8. data/lib/sequel/adapters/db2.rb +1 -0
  9. data/lib/sequel/adapters/dbi.rb +1 -0
  10. data/lib/sequel/adapters/fdbsql.rb +3 -2
  11. data/lib/sequel/adapters/firebird.rb +1 -0
  12. data/lib/sequel/adapters/ibmdb.rb +1 -21
  13. data/lib/sequel/adapters/informix.rb +1 -0
  14. data/lib/sequel/adapters/jdbc.rb +37 -49
  15. data/lib/sequel/adapters/jdbc/fdbsql.rb +1 -0
  16. data/lib/sequel/adapters/mysql.rb +5 -3
  17. data/lib/sequel/adapters/mysql2.rb +5 -2
  18. data/lib/sequel/adapters/odbc.rb +8 -4
  19. data/lib/sequel/adapters/openbase.rb +1 -0
  20. data/lib/sequel/adapters/oracle.rb +3 -46
  21. data/lib/sequel/adapters/postgres.rb +3 -36
  22. data/lib/sequel/adapters/shared/access.rb +1 -1
  23. data/lib/sequel/adapters/shared/fdbsql.rb +3 -3
  24. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  25. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +12 -44
  26. data/lib/sequel/adapters/shared/oracle.rb +6 -2
  27. data/lib/sequel/adapters/shared/postgres.rb +6 -6
  28. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  29. data/lib/sequel/adapters/sqlite.rb +3 -46
  30. data/lib/sequel/adapters/tinytds.rb +12 -28
  31. data/lib/sequel/adapters/utils/pg_types.rb +1 -1
  32. data/lib/sequel/connection_pool/sharded_threaded.rb +63 -16
  33. data/lib/sequel/connection_pool/threaded.rb +72 -18
  34. data/lib/sequel/core.rb +1 -1
  35. data/lib/sequel/database/connecting.rb +2 -2
  36. data/lib/sequel/database/misc.rb +5 -5
  37. data/lib/sequel/database/query.rb +3 -2
  38. data/lib/sequel/database/schema_generator.rb +19 -19
  39. data/lib/sequel/database/schema_methods.rb +2 -2
  40. data/lib/sequel/database/transactions.rb +3 -3
  41. data/lib/sequel/dataset/actions.rb +18 -8
  42. data/lib/sequel/dataset/graph.rb +2 -2
  43. data/lib/sequel/dataset/prepared_statements.rb +28 -1
  44. data/lib/sequel/dataset/query.rb +7 -7
  45. data/lib/sequel/exceptions.rb +27 -24
  46. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  47. data/lib/sequel/extensions/constraint_validations.rb +2 -2
  48. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  49. data/lib/sequel/extensions/pg_array.rb +10 -1
  50. data/lib/sequel/extensions/pg_row.rb +1 -1
  51. data/lib/sequel/extensions/pg_static_cache_updater.rb +1 -1
  52. data/lib/sequel/extensions/schema_dumper.rb +8 -8
  53. data/lib/sequel/extensions/split_array_nil.rb +1 -1
  54. data/lib/sequel/model.rb +1 -1
  55. data/lib/sequel/model/associations.rb +18 -11
  56. data/lib/sequel/model/base.rb +15 -15
  57. data/lib/sequel/model/exceptions.rb +11 -2
  58. data/lib/sequel/plugins/accessed_columns.rb +1 -1
  59. data/lib/sequel/plugins/auto_validations.rb +1 -1
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -1
  61. data/lib/sequel/plugins/class_table_inheritance.rb +4 -7
  62. data/lib/sequel/plugins/composition.rb +1 -1
  63. data/lib/sequel/plugins/constraint_validations.rb +2 -2
  64. data/lib/sequel/plugins/csv_serializer.rb +171 -0
  65. data/lib/sequel/plugins/dirty.rb +2 -2
  66. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  67. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  68. data/lib/sequel/plugins/many_through_many.rb +1 -1
  69. data/lib/sequel/plugins/nested_attributes.rb +5 -5
  70. data/lib/sequel/plugins/pg_array_associations.rb +4 -4
  71. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  72. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
  73. data/lib/sequel/plugins/serialization.rb +6 -6
  74. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  75. data/lib/sequel/plugins/sharding.rb +3 -1
  76. data/lib/sequel/plugins/single_table_inheritance.rb +5 -13
  77. data/lib/sequel/plugins/static_cache.rb +2 -2
  78. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  79. data/lib/sequel/plugins/tree.rb +1 -1
  80. data/lib/sequel/plugins/validation_class_methods.rb +2 -2
  81. data/lib/sequel/plugins/validation_helpers.rb +4 -4
  82. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  83. data/lib/sequel/sql.rb +1 -1
  84. data/lib/sequel/version.rb +1 -1
  85. data/spec/adapters/postgres_spec.rb +17 -0
  86. data/spec/core/connection_pool_spec.rb +1 -1
  87. data/spec/core/dataset_spec.rb +22 -0
  88. data/spec/extensions/auto_validations_spec.rb +1 -1
  89. data/spec/extensions/blacklist_security_spec.rb +2 -2
  90. data/spec/extensions/csv_serializer_spec.rb +173 -0
  91. data/spec/extensions/json_serializer_spec.rb +2 -2
  92. data/spec/extensions/nested_attributes_spec.rb +9 -9
  93. data/spec/extensions/pg_array_spec.rb +5 -0
  94. data/spec/extensions/single_table_inheritance_spec.rb +21 -0
  95. data/spec/extensions/touch_spec.rb +1 -1
  96. data/spec/extensions/tree_spec.rb +4 -0
  97. data/spec/extensions/xml_serializer_spec.rb +3 -3
  98. data/spec/integration/prepared_statement_test.rb +1 -1
  99. data/spec/integration/schema_test.rb +7 -0
  100. data/spec/integration/type_test.rb +2 -2
  101. data/spec/model/associations_spec.rb +108 -14
  102. data/spec/model/base_spec.rb +8 -8
  103. data/spec/model/record_spec.rb +7 -7
  104. metadata +6 -2
@@ -71,18 +71,24 @@ module Sequel
71
71
 
72
72
  # Return the number of rows modified by the given +sql+.
73
73
  def execute_dui(sql, opts=OPTS)
74
- execute(sql, opts.merge(:return=>:do))
74
+ opts = Hash[opts]
75
+ opts[:return] = :do
76
+ execute(sql, opts)
75
77
  end
76
78
 
77
79
  # Return the value of the autogenerated primary key (if any)
78
80
  # for the row inserted by the given +sql+.
79
81
  def execute_insert(sql, opts=OPTS)
80
- execute(sql, opts.merge(:return=>:insert))
82
+ opts = Hash[opts]
83
+ opts[:return] = :insert
84
+ execute(sql, opts)
81
85
  end
82
86
 
83
87
  # Execute the DDL +sql+ on the database and return nil.
84
88
  def execute_ddl(sql, opts=OPTS)
85
- execute(sql, opts.merge(:return=>:each))
89
+ opts = Hash[opts]
90
+ opts[:return] = :each
91
+ execute(sql, opts)
86
92
  nil
87
93
  end
88
94
 
@@ -202,30 +208,8 @@ module Sequel
202
208
  end
203
209
  end
204
210
 
205
- # SQLite prepared statement uses a new prepared statement each time
206
- # it is called, but it does use the bind arguments.
207
- module PreparedStatementMethods
208
- include ArgumentMapper
209
-
210
- private
211
-
212
- # Run execute_select on the database with the given SQL and the stored
213
- # bind arguments.
214
- def execute(sql, opts=OPTS, &block)
215
- super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
216
- end
217
-
218
- # Same as execute, explicit due to intricacies of alias and super.
219
- def execute_dui(sql, opts=OPTS, &block)
220
- super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
221
- end
222
-
223
- # Same as execute, explicit due to intricacies of alias and super.
224
- def execute_insert(sql, opts=OPTS, &block)
225
- super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
226
- end
227
- end
228
-
211
+ PreparedStatementMethods = prepared_statements_module("sql = prepared_sql; opts = Hash[opts]; opts[:arguments] = bind_arguments", ArgumentMapper)
212
+
229
213
  # Yield hashes with symbol keys, attempting to optimize for
230
214
  # various cases.
231
215
  def fetch_rows(sql)
@@ -238,7 +222,7 @@ module Sequel
238
222
  result.each(*args) do |r|
239
223
  unless cols
240
224
  cols = result.fields.map{|c| [c, output_identifier(c)]}
241
- @columns = columns = cols.map{|c| c.last}
225
+ @columns = columns = cols.map(&:last)
242
226
  end
243
227
  h = {}
244
228
  cols.each do |s, sym|
@@ -24,7 +24,7 @@ module Sequel
24
24
  s.to_f
25
25
  end
26
26
  end
27
- def date(s) ::Date.new(*s.split(DASH_STR).map{|x| x.to_i}) end
27
+ def date(s) ::Date.new(*s.split(DASH_STR).map(&:to_i)) end
28
28
  def bytea(str)
29
29
  str = if str =~ /\A\\x/
30
30
  # PostgreSQL 9.0+ bytea hex format
@@ -18,6 +18,12 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
18
18
  @available_connections = {}
19
19
  @connections_to_remove = []
20
20
  @servers = opts.fetch(:servers_hash, Hash.new(:default))
21
+
22
+ if USE_WAITER
23
+ @waiter = nil
24
+ @waiters = {}
25
+ end
26
+
21
27
  add_servers([:default])
22
28
  add_servers(opts[:servers].keys) if opts[:servers]
23
29
  end
@@ -32,6 +38,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
32
38
  @servers[server] = server
33
39
  @available_connections[server] = []
34
40
  @allocated[server] = {}
41
+ @waiters[server] = ConditionVariable.new if USE_WAITER
35
42
  end
36
43
  end
37
44
  end
@@ -115,16 +122,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
115
122
  return yield(conn)
116
123
  end
117
124
  begin
118
- unless conn = acquire(t, server)
119
- time = Time.now
120
- timeout = time + @timeout
121
- sleep_time = @sleep_time
122
- sleep sleep_time
123
- until conn = acquire(t, server)
124
- raise(::Sequel::PoolTimeout) if Time.now > timeout
125
- sleep sleep_time
126
- end
127
- end
125
+ conn = acquire(t, server)
128
126
  yield conn
129
127
  rescue Sequel::DatabaseDisconnectError
130
128
  sync{@connections_to_remove << conn} if conn
@@ -144,6 +142,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
144
142
  servers.each do |server|
145
143
  if @servers.include?(server)
146
144
  disconnect_server(server)
145
+ @waiters.delete(server) if USE_WAITER
147
146
  @available_connections.delete(server)
148
147
  @allocated.delete(server)
149
148
  @servers.delete(server)
@@ -164,16 +163,60 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
164
163
  private
165
164
 
166
165
  # Assigns a connection to the supplied thread for the given server, if one
167
- # is available. The calling code should NOT already have the mutex when
166
+ # is available. The calling code should already have the mutex when
168
167
  # calling this.
169
- def acquire(thread, server)
170
- sync do
171
- if conn = available(server)
172
- allocated(server)[thread] = conn
173
- end
168
+ def _acquire(thread, server)
169
+ if conn = available(server)
170
+ allocated(server)[thread] = conn
174
171
  end
175
172
  end
176
173
 
174
+ if USE_WAITER
175
+ # Assigns a connection to the supplied thread, if one
176
+ # is available. The calling code should NOT already have the mutex when
177
+ # calling this.
178
+ #
179
+ # This should return a connection is one is available within the timeout,
180
+ # or nil if a connection could not be acquired within the timeout.
181
+ def acquire(thread, server)
182
+ sync do
183
+ if conn = _acquire(thread, server)
184
+ return conn
185
+ end
186
+
187
+ time = Time.now
188
+ @waiters[server].wait(@mutex, @timeout)
189
+ Thread.pass
190
+
191
+ until conn = _acquire(thread, server)
192
+ deadline ||= time + @timeout
193
+ current_time = Time.now
194
+ raise(::Sequel::PoolTimeout, "timeout: #{@timeout}, elapsed: #{current_time - time}") if current_time > deadline
195
+ @waiters[server].wait(@mutex, deadline - current_time)
196
+ Thread.pass
197
+ end
198
+
199
+ conn
200
+ end
201
+ end
202
+ else
203
+ # :nocov:
204
+ def acquire(thread, server)
205
+ unless conn = sync{_acquire(thread, server)}
206
+ time = Time.now
207
+ timeout = time + @timeout
208
+ sleep_time = @sleep_time
209
+ sleep sleep_time
210
+ until conn = sync{_acquire(thread, server)}
211
+ raise(::Sequel::PoolTimeout, "timeout: #{@timeout}, elapsed: #{Time.now - time}") if Time.now > timeout
212
+ sleep sleep_time
213
+ end
214
+ end
215
+ conn
216
+ end
217
+ # :nocov:
218
+ end
219
+
177
220
  # Returns an available connection to the given server. If no connection is
178
221
  # available, tries to create a new connection. The calling code should already
179
222
  # have the mutex before calling this.
@@ -186,6 +229,10 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
186
229
  # before calling this.
187
230
  def checkin_connection(server, conn)
188
231
  available_connections(server) << conn
232
+ if USE_WAITER
233
+ @waiters[server].signal
234
+ Thread.pass
235
+ end
189
236
  conn
190
237
  end
191
238
 
@@ -1,6 +1,11 @@
1
1
  # A connection pool allowing multi-threaded access to a pool of connections.
2
2
  # This is the default connection pool used by Sequel.
3
3
  class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
4
+ # Whether or not a ConditionVariable should be used to wait for connections.
5
+ # True except on ruby 1.8, where ConditionVariable#wait does not support a
6
+ # timeout.
7
+ USE_WAITER = RUBY_VERSION >= '1.9'
8
+
4
9
  # The maximum number of connections this pool will create (per shard/server
5
10
  # if sharding).
6
11
  attr_reader :max_size
@@ -19,7 +24,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
19
24
  # * :max_connections - The maximum number of connections the connection pool
20
25
  # will open (default 4)
21
26
  # * :pool_sleep_time - The amount of time to sleep before attempting to acquire
22
- # a connection again (default 0.001)
27
+ # a connection again, only used on ruby 1.8. (default 0.001)
23
28
  # * :pool_timeout - The amount of seconds to wait to acquire a connection
24
29
  # before raising a PoolTimeoutError (default 5)
25
30
  def initialize(db, opts = OPTS)
@@ -31,7 +36,12 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
31
36
  @available_connections = []
32
37
  @allocated = {}
33
38
  @timeout = Float(opts[:pool_timeout] || 5)
34
- @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
39
+
40
+ if USE_WAITER
41
+ @waiter = ConditionVariable.new
42
+ else
43
+ @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
44
+ end
35
45
  end
36
46
 
37
47
  # Yield all of the available connections, and the one currently allocated to
@@ -85,16 +95,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
85
95
  return yield(conn)
86
96
  end
87
97
  begin
88
- unless conn = acquire(t)
89
- time = Time.now
90
- timeout = time + @timeout
91
- sleep_time = @sleep_time
92
- sleep sleep_time
93
- until conn = acquire(t)
94
- raise(::Sequel::PoolTimeout) if Time.now > timeout
95
- sleep sleep_time
96
- end
97
- end
98
+ conn = acquire(t)
98
99
  yield conn
99
100
  rescue Sequel::DatabaseDisconnectError
100
101
  oconn = conn
@@ -120,16 +121,65 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
120
121
  private
121
122
 
122
123
  # Assigns a connection to the supplied thread, if one
123
- # is available. The calling code should NOT already have the mutex when
124
+ # is available. The calling code should already have the mutex when
124
125
  # calling this.
125
- def acquire(thread)
126
- sync do
127
- if conn = available
128
- @allocated[thread] = conn
129
- end
126
+ def _acquire(thread)
127
+ if conn = available
128
+ @allocated[thread] = conn
130
129
  end
131
130
  end
132
131
 
132
+ if USE_WAITER
133
+ # Assigns a connection to the supplied thread, if one
134
+ # is available. The calling code should NOT already have the mutex when
135
+ # calling this.
136
+ #
137
+ # This should return a connection is one is available within the timeout,
138
+ # or nil if a connection could not be acquired within the timeout.
139
+ def acquire(thread)
140
+ sync do
141
+ if conn = _acquire(thread)
142
+ return conn
143
+ end
144
+
145
+ time = Time.now
146
+ @waiter.wait(@mutex, @timeout)
147
+
148
+ # Not sure why this is helpful, but calling Thread.pass after conditional
149
+ # variable access dramatically increases reliability when under heavy
150
+ # resource contention (almost eliminating timeouts), at a small cost to
151
+ # runtime performance.
152
+ Thread.pass
153
+
154
+ until conn = _acquire(thread)
155
+ deadline ||= time + @timeout
156
+ current_time = Time.now
157
+ raise(::Sequel::PoolTimeout, "timeout: #{@timeout}, elapsed: #{current_time - time}") if current_time > deadline
158
+ @waiter.wait(@mutex, deadline - current_time)
159
+ Thread.pass
160
+ end
161
+
162
+ conn
163
+ end
164
+ end
165
+ else
166
+ # :nocov:
167
+ def acquire(thread)
168
+ unless conn = sync{_acquire(thread)}
169
+ time = Time.now
170
+ timeout = time + @timeout
171
+ sleep_time = @sleep_time
172
+ sleep sleep_time
173
+ until conn = sync{_acquire(thread)}
174
+ raise(::Sequel::PoolTimeout, "timeout: #{@timeout}, elapsed: #{Time.now - time}") if Time.now > timeout
175
+ sleep sleep_time
176
+ end
177
+ end
178
+ conn
179
+ end
180
+ # :nocov:
181
+ end
182
+
133
183
  # Returns an available connection. If no connection is
134
184
  # available, tries to create a new connection. The calling code should already
135
185
  # have the mutex before calling this.
@@ -141,6 +191,10 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
141
191
  # The calling code should already have the mutex before calling this.
142
192
  def checkin_connection(conn)
143
193
  @available_connections << conn
194
+ if USE_WAITER
195
+ @waiter.signal
196
+ Thread.pass
197
+ end
144
198
  conn
145
199
  end
146
200
 
data/lib/sequel/core.rb CHANGED
@@ -328,7 +328,7 @@ module Sequel
328
328
  def self.transaction(dbs, opts=OPTS, &block)
329
329
  unless opts[:rollback]
330
330
  rescue_rollback = true
331
- opts = opts.merge(:rollback=>:reraise)
331
+ opts = Hash[opts].merge!(:rollback=>:reraise)
332
332
  end
333
333
  pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
334
334
  if rescue_rollback
@@ -6,7 +6,7 @@ module Sequel
6
6
  # ---------------------
7
7
 
8
8
  # Array of supported database adapters
9
- ADAPTERS = %w'ado amalgalite cubrid db2 dbi do fdbsql firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect{|x| x.to_sym}
9
+ ADAPTERS = %w'ado amalgalite cubrid db2 dbi do fdbsql firebird ibmdb informix jdbc mock mysql mysql2 odbc openbase oracle postgres sqlanywhere sqlite swift tinytds'.collect(&:to_sym)
10
10
 
11
11
  @single_threaded = false
12
12
 
@@ -38,7 +38,7 @@ module Sequel
38
38
  if match = /\A(jdbc|do):/o.match(conn_string)
39
39
  c = adapter_class(match[1].to_sym)
40
40
  opts = opts.merge(:orig_opts=>opts.dup)
41
- opts = {:uri=>conn_string}.merge(opts)
41
+ opts = {:uri=>conn_string}.merge!(opts)
42
42
  else
43
43
  uri = URI.parse(conn_string)
44
44
  scheme = uri.scheme
@@ -390,11 +390,11 @@ module Sequel
390
390
  nil
391
391
  end
392
392
 
393
- NOT_NULL_CONSTRAINT_SQLSTATES = %w'23502'.freeze.each{|s| s.freeze}
394
- FOREIGN_KEY_CONSTRAINT_SQLSTATES = %w'23503 23506 23504'.freeze.each{|s| s.freeze}
395
- UNIQUE_CONSTRAINT_SQLSTATES = %w'23505'.freeze.each{|s| s.freeze}
396
- CHECK_CONSTRAINT_SQLSTATES = %w'23513 23514'.freeze.each{|s| s.freeze}
397
- SERIALIZATION_CONSTRAINT_SQLSTATES = %w'40001'.freeze.each{|s| s.freeze}
393
+ NOT_NULL_CONSTRAINT_SQLSTATES = %w'23502'.freeze.each(&:freeze)
394
+ FOREIGN_KEY_CONSTRAINT_SQLSTATES = %w'23503 23506 23504'.freeze.each(&:freeze)
395
+ UNIQUE_CONSTRAINT_SQLSTATES = %w'23505'.freeze.each(&:freeze)
396
+ CHECK_CONSTRAINT_SQLSTATES = %w'23513 23514'.freeze.each(&:freeze)
397
+ SERIALIZATION_CONSTRAINT_SQLSTATES = %w'40001'.freeze.each(&:freeze)
398
398
  # Given the SQLState, return the appropriate DatabaseError subclass.
399
399
  def database_specific_error_class_from_sqlstate(sqlstate)
400
400
  case sqlstate
@@ -91,7 +91,8 @@ module Sequel
91
91
  #
92
92
  # :allow_null :: Whether NULL is an allowed value for the column.
93
93
  # :db_type :: The database type for the column, as a database specific string.
94
- # :default :: The database default for the column, as a database specific string.
94
+ # :default :: The database default for the column, as a database specific string, or nil if there is
95
+ # no default value.
95
96
  # :primary_key :: Whether the columns is a primary key column. If this column is not present,
96
97
  # it means that primary key information is unavailable, not that the column
97
98
  # is not a primary key.
@@ -166,7 +167,7 @@ module Sequel
166
167
  end
167
168
 
168
169
  cols.each do |_,c|
169
- c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type])
170
+ c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type]) unless c.has_key?(:ruby_default)
170
171
  if c[:primary_key] && !auto_increment_set
171
172
  # If adapter didn't set it, assume that integer primary keys are auto incrementing
172
173
  c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/io)
@@ -109,7 +109,7 @@ module Sequel
109
109
  # creating a unique index on the column.
110
110
  # :unique_constraint_name :: The name to give the unique key constraint
111
111
  def column(name, type, opts = OPTS)
112
- columns << {:name => name, :type => type}.merge(opts)
112
+ columns << {:name => name, :type => type}.merge!(opts)
113
113
  if index_opts = opts[:index]
114
114
  index(name, index_opts.is_a?(Hash) ? index_opts : {})
115
115
  end
@@ -206,7 +206,7 @@ module Sequel
206
206
  # index [:artist_id, :name]
207
207
  # # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
208
208
  def index(columns, opts = OPTS)
209
- indexes << {:columns => Array(columns)}.merge(opts)
209
+ indexes << {:columns => Array(columns)}.merge!(opts)
210
210
  end
211
211
 
212
212
  # Add a column with the given type, name, and opts to the DDL. See +column+ for available
@@ -267,7 +267,7 @@ module Sequel
267
267
  # Supports the same :deferrable option as #column. The :name option can be used
268
268
  # to name the constraint.
269
269
  def unique(columns, opts = OPTS)
270
- constraints << {:type => :unique, :columns => Array(columns)}.merge(opts)
270
+ constraints << {:type => :unique, :columns => Array(columns)}.merge!(opts)
271
271
  end
272
272
 
273
273
  private
@@ -275,12 +275,12 @@ module Sequel
275
275
  # Add a composite primary key constraint
276
276
  def composite_primary_key(columns, *args)
277
277
  opts = args.pop || {}
278
- constraints << {:type => :primary_key, :columns => columns}.merge(opts)
278
+ constraints << {:type => :primary_key, :columns => columns}.merge!(opts)
279
279
  end
280
280
 
281
281
  # Add a composite foreign key constraint
282
282
  def composite_foreign_key(columns, opts)
283
- constraints << {:type => :foreign_key, :columns => columns}.merge(opts)
283
+ constraints << {:type => :foreign_key, :columns => columns}.merge!(opts)
284
284
  end
285
285
 
286
286
  add_type_method(*GENERIC_TYPES)
@@ -315,7 +315,7 @@ module Sequel
315
315
  #
316
316
  # add_column(:name, String) # ADD COLUMN name varchar(255)
317
317
  def add_column(name, type, opts = OPTS)
318
- @operations << {:op => :add_column, :name => name, :type => type}.merge(opts)
318
+ @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
319
319
  end
320
320
 
321
321
  # Add a constraint with the given name and args to the DDL for the table.
@@ -337,7 +337,7 @@ module Sequel
337
337
  #
338
338
  # Supports the same :deferrable option as CreateTableGenerator#column.
339
339
  def add_unique_constraint(columns, opts = OPTS)
340
- @operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge(opts)
340
+ @operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
341
341
  end
342
342
 
343
343
  # Add a foreign key with the given name and referencing the given table
@@ -364,13 +364,13 @@ module Sequel
364
364
  # sense when using an array of columns.
365
365
  def add_foreign_key(name, table, opts = OPTS)
366
366
  return add_composite_foreign_key(name, table, opts) if name.is_a?(Array)
367
- add_column(name, Integer, {:table=>table}.merge(opts))
367
+ add_column(name, Integer, {:table=>table}.merge!(opts))
368
368
  end
369
369
 
370
370
  # Add a full text index on the given columns to the DDL for the table.
371
371
  # See CreateTableGenerator#index for available options.
372
372
  def add_full_text_index(columns, opts = OPTS)
373
- add_index(columns, {:type=>:full_text}.merge(opts))
373
+ add_index(columns, {:type=>:full_text}.merge!(opts))
374
374
  end
375
375
 
376
376
  # Add an index on the given columns to the DDL for the table. See
@@ -378,7 +378,7 @@ module Sequel
378
378
  #
379
379
  # add_index(:artist_id) # CREATE INDEX table_artist_id_index ON table (artist_id)
380
380
  def add_index(columns, opts = OPTS)
381
- @operations << {:op => :add_index, :columns => Array(columns)}.merge(opts)
381
+ @operations << {:op => :add_index, :columns => Array(columns)}.merge!(opts)
382
382
  end
383
383
 
384
384
  # Add a primary key to the DDL for the table. See CreateTableGenerator#column
@@ -396,7 +396,7 @@ module Sequel
396
396
  # Add a spatial index on the given columns to the DDL for the table.
397
397
  # See CreateTableGenerator#index for available options.
398
398
  def add_spatial_index(columns, opts = OPTS)
399
- add_index(columns, {:type=>:spatial}.merge(opts))
399
+ add_index(columns, {:type=>:spatial}.merge!(opts))
400
400
  end
401
401
 
402
402
  # Remove a column from the DDL for the table.
@@ -404,7 +404,7 @@ module Sequel
404
404
  # drop_column(:artist_id) # DROP COLUMN artist_id
405
405
  # drop_column(:artist_id, :cascade=>true) # DROP COLUMN artist_id CASCADE
406
406
  def drop_column(name, opts=OPTS)
407
- @operations << {:op => :drop_column, :name => name}.merge(opts)
407
+ @operations << {:op => :drop_column, :name => name}.merge!(opts)
408
408
  end
409
409
 
410
410
  # Remove a constraint from the DDL for the table. MySQL/SQLite specific options:
@@ -415,7 +415,7 @@ module Sequel
415
415
  # drop_constraint(:unique_name) # DROP CONSTRAINT unique_name
416
416
  # drop_constraint(:unique_name, :cascade=>true) # DROP CONSTRAINT unique_name CASCADE
417
417
  def drop_constraint(name, opts=OPTS)
418
- @operations << {:op => :drop_constraint, :name => name}.merge(opts)
418
+ @operations << {:op => :drop_constraint, :name => name}.merge!(opts)
419
419
  end
420
420
 
421
421
  # Remove a foreign key and the associated column from the DDL for the table. General options:
@@ -449,14 +449,14 @@ module Sequel
449
449
  # drop_index([:a, :b]) # DROP INDEX table_a_b_index
450
450
  # drop_index([:a, :b], :name=>:foo) # DROP INDEX foo
451
451
  def drop_index(columns, options=OPTS)
452
- @operations << {:op => :drop_index, :columns => Array(columns)}.merge(options)
452
+ @operations << {:op => :drop_index, :columns => Array(columns)}.merge!(options)
453
453
  end
454
454
 
455
455
  # Modify a column's name in the DDL for the table.
456
456
  #
457
457
  # rename_column(:name, :artist_name) # RENAME COLUMN name TO artist_name
458
458
  def rename_column(name, new_name, opts = OPTS)
459
- @operations << {:op => :rename_column, :name => name, :new_name => new_name}.merge(opts)
459
+ @operations << {:op => :rename_column, :name => name, :new_name => new_name}.merge!(opts)
460
460
  end
461
461
 
462
462
  # Modify a column's default value in the DDL for the table.
@@ -480,7 +480,7 @@ module Sequel
480
480
  # On MySQL, make sure to use a symbol for the name of the column, as otherwise you
481
481
  # can lose the default and NULL/NOT NULL setting for the column.
482
482
  def set_column_type(name, type, opts=OPTS)
483
- @operations << {:op => :set_column_type, :name => name, :type => type}.merge(opts)
483
+ @operations << {:op => :set_column_type, :name => name, :type => type}.merge!(opts)
484
484
  end
485
485
 
486
486
  # Set a given column as allowing NULL values.
@@ -507,17 +507,17 @@ module Sequel
507
507
 
508
508
  # Add a composite primary key constraint
509
509
  def add_composite_primary_key(columns, opts)
510
- @operations << {:op => :add_constraint, :type => :primary_key, :columns => columns}.merge(opts)
510
+ @operations << {:op => :add_constraint, :type => :primary_key, :columns => columns}.merge!(opts)
511
511
  end
512
512
 
513
513
  # Add a composite foreign key constraint
514
514
  def add_composite_foreign_key(columns, table, opts)
515
- @operations << {:op => :add_constraint, :type => :foreign_key, :columns => columns, :table => table}.merge(opts)
515
+ @operations << {:op => :add_constraint, :type => :foreign_key, :columns => columns, :table => table}.merge!(opts)
516
516
  end
517
517
 
518
518
  # Drop a composite foreign key constraint
519
519
  def drop_composite_foreign_key(columns, opts)
520
- @operations << {:op => :drop_constraint, :type => :foreign_key, :columns => columns}.merge(opts)
520
+ @operations << {:op => :drop_constraint, :type => :foreign_key, :columns => columns}.merge!(opts)
521
521
  end
522
522
  end
523
523
  end