sequel 4.21.0 → 4.22.0

Sign up to get free protection for your applications and to get access to all the features.
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