sequel 3.38.0 → 3.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/CHANGELOG +62 -0
  2. data/README.rdoc +2 -2
  3. data/bin/sequel +12 -2
  4. data/doc/advanced_associations.rdoc +1 -1
  5. data/doc/association_basics.rdoc +13 -0
  6. data/doc/release_notes/3.39.0.txt +237 -0
  7. data/doc/schema_modification.rdoc +4 -4
  8. data/lib/sequel/adapters/jdbc/derby.rb +1 -0
  9. data/lib/sequel/adapters/mock.rb +5 -0
  10. data/lib/sequel/adapters/mysql.rb +8 -1
  11. data/lib/sequel/adapters/mysql2.rb +10 -3
  12. data/lib/sequel/adapters/postgres.rb +72 -8
  13. data/lib/sequel/adapters/shared/db2.rb +1 -0
  14. data/lib/sequel/adapters/shared/mssql.rb +57 -0
  15. data/lib/sequel/adapters/shared/mysql.rb +95 -19
  16. data/lib/sequel/adapters/shared/oracle.rb +14 -0
  17. data/lib/sequel/adapters/shared/postgres.rb +63 -24
  18. data/lib/sequel/adapters/shared/sqlite.rb +6 -9
  19. data/lib/sequel/connection_pool/sharded_threaded.rb +8 -3
  20. data/lib/sequel/connection_pool/threaded.rb +9 -4
  21. data/lib/sequel/database/query.rb +60 -48
  22. data/lib/sequel/database/schema_generator.rb +13 -6
  23. data/lib/sequel/database/schema_methods.rb +65 -12
  24. data/lib/sequel/dataset/actions.rb +22 -4
  25. data/lib/sequel/dataset/features.rb +5 -0
  26. data/lib/sequel/dataset/graph.rb +2 -3
  27. data/lib/sequel/dataset/misc.rb +2 -2
  28. data/lib/sequel/dataset/query.rb +0 -2
  29. data/lib/sequel/dataset/sql.rb +33 -12
  30. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  31. data/lib/sequel/extensions/eval_inspect.rb +17 -2
  32. data/lib/sequel/extensions/pg_array_ops.rb +15 -5
  33. data/lib/sequel/extensions/pg_interval.rb +2 -2
  34. data/lib/sequel/extensions/pg_row_ops.rb +18 -0
  35. data/lib/sequel/extensions/schema_dumper.rb +3 -11
  36. data/lib/sequel/model/associations.rb +3 -2
  37. data/lib/sequel/model/base.rb +57 -13
  38. data/lib/sequel/model/exceptions.rb +20 -2
  39. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  40. data/lib/sequel/plugins/defaults_setter.rb +15 -1
  41. data/lib/sequel/plugins/dirty.rb +2 -2
  42. data/lib/sequel/plugins/identity_map.rb +12 -8
  43. data/lib/sequel/plugins/subclasses.rb +19 -1
  44. data/lib/sequel/plugins/tree.rb +3 -3
  45. data/lib/sequel/plugins/validation_helpers.rb +24 -4
  46. data/lib/sequel/sql.rb +64 -24
  47. data/lib/sequel/timezones.rb +10 -2
  48. data/lib/sequel/version.rb +1 -1
  49. data/spec/adapters/mssql_spec.rb +26 -25
  50. data/spec/adapters/mysql_spec.rb +57 -23
  51. data/spec/adapters/oracle_spec.rb +34 -49
  52. data/spec/adapters/postgres_spec.rb +226 -128
  53. data/spec/adapters/sqlite_spec.rb +50 -49
  54. data/spec/core/connection_pool_spec.rb +22 -0
  55. data/spec/core/database_spec.rb +53 -47
  56. data/spec/core/dataset_spec.rb +36 -32
  57. data/spec/core/expression_filters_spec.rb +14 -2
  58. data/spec/core/mock_adapter_spec.rb +4 -0
  59. data/spec/core/object_graph_spec.rb +0 -13
  60. data/spec/core/schema_spec.rb +64 -5
  61. data/spec/core_extensions_spec.rb +1 -0
  62. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  63. data/spec/extensions/constraint_validations_spec.rb +316 -0
  64. data/spec/extensions/defaults_setter_spec.rb +24 -0
  65. data/spec/extensions/eval_inspect_spec.rb +9 -0
  66. data/spec/extensions/identity_map_spec.rb +11 -2
  67. data/spec/extensions/pg_array_ops_spec.rb +9 -0
  68. data/spec/extensions/pg_row_ops_spec.rb +11 -1
  69. data/spec/extensions/pg_row_plugin_spec.rb +4 -0
  70. data/spec/extensions/schema_dumper_spec.rb +8 -5
  71. data/spec/extensions/subclasses_spec.rb +14 -0
  72. data/spec/extensions/validation_helpers_spec.rb +15 -2
  73. data/spec/integration/dataset_test.rb +75 -1
  74. data/spec/integration/plugin_test.rb +146 -0
  75. data/spec/integration/schema_test.rb +34 -0
  76. data/spec/model/dataset_methods_spec.rb +38 -0
  77. data/spec/model/hooks_spec.rb +6 -0
  78. data/spec/model/validations_spec.rb +27 -2
  79. metadata +8 -2
@@ -300,7 +300,7 @@ module Sequel
300
300
  # Pretend tinyint is another integer type if its length is not 1, to
301
301
  # avoid casting to boolean if Sequel::MySQL.convert_tinyint_to_bool
302
302
  # is set.
303
- type_proc = f.type == 1 && f.length != 1 ? cps[2] : cps[f.type]
303
+ type_proc = f.type == 1 && cast_tinyint_integer?(f) ? cps[2] : cps[f.type]
304
304
  [output_identifier(f.name), type_proc, i+=1]
305
305
  end
306
306
  @columns = cols.map{|c| c.first}
@@ -338,6 +338,13 @@ module Sequel
338
338
  end
339
339
 
340
340
  private
341
+
342
+ # Whether a tinyint field should be casted as an integer. By default,
343
+ # casts to integer if the field length is not 1. Can be overwritten
344
+ # to make tinyint casting dataset dependent.
345
+ def cast_tinyint_integer?(field)
346
+ field.length != 1
347
+ end
341
348
 
342
349
  # Set the :type option to :select if it hasn't been set.
343
350
  def execute(sql, opts={}, &block)
@@ -87,7 +87,7 @@ module Sequel
87
87
  # yield the connection if a block is given.
88
88
  def _execute(conn, sql, opts)
89
89
  begin
90
- r = log_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql){conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :cast_booleans => convert_tinyint_to_bool)}
90
+ r = log_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql){conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone)}
91
91
  if opts[:type] == :select
92
92
  yield r if r
93
93
  elsif block_given?
@@ -150,7 +150,7 @@ module Sequel
150
150
  cols = r.fields
151
151
  @columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
152
152
  cs = cols.zip(cols2)
153
- r.each do |row|
153
+ r.each(:cast_booleans=>convert_tinyint_to_bool?) do |row|
154
154
  h = {}
155
155
  cs.each do |a, b|
156
156
  h[b] = row[a]
@@ -159,7 +159,7 @@ module Sequel
159
159
  end
160
160
  else
161
161
  @columns = r.fields
162
- r.each{|h| yield h}
162
+ r.each(:cast_booleans=>convert_tinyint_to_bool?){|h| yield h}
163
163
  end
164
164
  end
165
165
  self
@@ -167,6 +167,13 @@ module Sequel
167
167
 
168
168
  private
169
169
 
170
+ # Whether to cast tinyint(1) columns to integer instead of boolean.
171
+ # By default, uses the opposite of the database's convert_tinyint_to_bool
172
+ # setting. Exists for compatibility with the mysql adapter.
173
+ def convert_tinyint_to_bool?
174
+ @db.convert_tinyint_to_bool
175
+ end
176
+
170
177
  # Set the :type option to :select if it hasn't been set.
171
178
  def execute(sql, opts={}, &block)
172
179
  super(sql, {:type=>:select}.merge(opts), &block)
@@ -133,7 +133,7 @@ module Sequel
133
133
  begin
134
134
  block_given? ? yield(q) : q.cmd_tuples
135
135
  ensure
136
- q.clear if q
136
+ q.clear if q && q.respond_to?(:clear)
137
137
  end
138
138
  end
139
139
 
@@ -235,13 +235,11 @@ module Sequel
235
235
  end
236
236
 
237
237
  if SEQUEL_POSTGRES_USES_PG
238
- # +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
238
+ # +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
239
239
  # results directly to the caller. This method is only supported if pg is the
240
240
  # underlying ruby driver. This method should only be called if you want
241
- # results returned to the client. If you are using +COPY FROM+ or +COPY TO+
242
- # with a filename, you should just use +run+ instead of this method. This
243
- # method does not currently support +COPY FROM STDIN+, but that may be supported
244
- # in the future.
241
+ # results returned to the client. If you are using +COPY TO+
242
+ # with a filename, you should just use +run+ instead of this method.
245
243
  #
246
244
  # The table argument supports the following types:
247
245
  #
@@ -297,6 +295,67 @@ module Sequel
297
295
  end
298
296
  end
299
297
 
298
+ # +copy_into+ uses PostgreSQL's +COPY FROM STDIN+ SQL statement to do very fast inserts
299
+ # into a table using input preformatting in either CSV or PostgreSQL text format.
300
+ # This method is only supported if pg is the underlying ruby driver. This method should only be called if you want
301
+ # results returned to the client. If you are using +COPY FROM+
302
+ # with a filename, you should just use +run+ instead of this method.
303
+ #
304
+ # The following options are respected:
305
+ #
306
+ # :columns :: The columns to insert into, with the same order as the columns in the
307
+ # input data. If this isn't given, uses all columns in the table.
308
+ # :data :: The data to copy to PostgreSQL, which should already be in CSV or PostgreSQL
309
+ # text format. This can be either a string, or any object that responds to
310
+ # each and yields string.
311
+ # :format :: The format to use. text is the default, so this should be :csv or :binary.
312
+ # :options :: An options SQL string to use, which should contain comma separated options.
313
+ # :server :: The server on which to run the query.
314
+ #
315
+ # If a block is provided and :data option is not, this will yield to the block repeatedly.
316
+ # The block should return a string, or nil to signal that it is finished.
317
+ def copy_into(table, opts={})
318
+ sql = "COPY #{literal(table)}"
319
+ if cols = opts[:columns]
320
+ sql << literal(Array(cols))
321
+ end
322
+ sql << " FROM STDIN"
323
+ if opts[:options] || opts[:format]
324
+ sql << " ("
325
+ sql << "FORMAT #{opts[:format]}" if opts[:format]
326
+ sql << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
327
+ sql << ')'
328
+ end
329
+
330
+ data = opts[:data]
331
+ data = Array(data) if data.is_a?(String)
332
+
333
+ if block_given? && data
334
+ raise Error, "Cannot provide both a :data option and a block to copy_into"
335
+ elsif !block_given? && !data
336
+ raise Error, "Must provide either a :data option or a block to copy_into"
337
+ end
338
+
339
+ synchronize(opts[:server]) do |conn|
340
+ conn.execute(sql)
341
+ begin
342
+ if block_given?
343
+ while buf = yield
344
+ conn.put_copy_data(buf)
345
+ end
346
+ else
347
+ data.each{|buf| conn.put_copy_data(buf)}
348
+ end
349
+ rescue Exception => e
350
+ conn.put_copy_end("ruby exception occurred while copying data into PostgreSQL")
351
+ raise
352
+ ensure
353
+ conn.put_copy_end unless e
354
+ conn.get_result
355
+ end
356
+ end
357
+ end
358
+
300
359
  # Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
301
360
  # After a notification is received, or the timeout has passed, stops listening to the channel. Options:
302
361
  #
@@ -373,6 +432,11 @@ module Sequel
373
432
  end
374
433
  end
375
434
 
435
+ # Execute the prepared statement name with the given arguments on the connection.
436
+ def _execute_prepared_statement(conn, ps_name, args, opts)
437
+ conn.exec_prepared(ps_name, args)
438
+ end
439
+
376
440
  # Convert exceptions raised from the block into DatabaseErrors.
377
441
  def check_database_errors
378
442
  begin
@@ -426,11 +490,11 @@ module Sequel
426
490
  log_sql << ")"
427
491
  end
428
492
 
429
- q = conn.check_disconnect_errors{log_yield(log_sql, args){conn.exec_prepared(ps_name, args)}}
493
+ q = conn.check_disconnect_errors{log_yield(log_sql, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
430
494
  begin
431
495
  block_given? ? yield(q) : q.cmd_tuples
432
496
  ensure
433
- q.clear
497
+ q.clear if q && q.respond_to?(:clear)
434
498
  end
435
499
  end
436
500
 
@@ -197,6 +197,7 @@ module Sequel
197
197
  PAREN_CLOSE = Dataset::PAREN_CLOSE
198
198
  PAREN_OPEN = Dataset::PAREN_OPEN
199
199
  BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR, :'B~'=>:BITNOT}
200
+ EMULATED_FUNCTION_MAP = {:char_length=>'length'.freeze}
200
201
  BOOL_TRUE = '1'.freeze
201
202
  BOOL_FALSE = '0'.freeze
202
203
  CAST_STRING_OPEN = "RTRIM(CHAR(".freeze
@@ -12,6 +12,7 @@ module Sequel
12
12
  SQL_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION".freeze
13
13
  SQL_ROLLBACK_TO_SAVEPOINT = 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION autopoint_%d'.freeze
14
14
  SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
15
+ MSSQL_DEFAULT_RE = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/
15
16
 
16
17
  # Whether to use N'' to quote strings, which allows unicode characters inside the
17
18
  # strings. True by default for compatibility, can be set to false for a possible
@@ -109,6 +110,39 @@ module Sequel
109
110
  AUTO_INCREMENT
110
111
  end
111
112
 
113
+ # Preprocess the array of operations. If it looks like some operations depend
114
+ # on results of earlier operations and may require reloading the schema to
115
+ # work correctly, split those operations into separate lists, and between each
116
+ # list, remove the cached schema so that the later operations deal with the
117
+ # then current table schema.
118
+ def apply_alter_table(name, ops)
119
+ modified_columns = []
120
+ op_groups = [[]]
121
+ ops.each do |op|
122
+ case op[:op]
123
+ when :add_column, :set_column_type, :set_column_null
124
+ if modified_columns.include?(op[:name])
125
+ op_groups << []
126
+ else
127
+ modified_columns << op[:name]
128
+ end
129
+ when :rename_column
130
+ if modified_columns.include?(op[:name]) || modified_columns.include?(op[:new_name])
131
+ op_groups << []
132
+ end
133
+ modified_columns << op[:name] unless modified_columns.include?(op[:name])
134
+ modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name])
135
+ end
136
+ op_groups.last << op
137
+ end
138
+
139
+ op_groups.each do |ops|
140
+ next if ops.empty?
141
+ alter_table_sql_list(name, ops).each{|sql| execute_ddl(sql)}
142
+ remove_cached_schema(name)
143
+ end
144
+ end
145
+
112
146
  # MSSQL specific syntax for altering tables.
113
147
  def alter_table_sql(table, op)
114
148
  case op[:op]
@@ -158,6 +192,14 @@ module Sequel
158
192
  SQL_BEGIN
159
193
  end
160
194
 
195
+ # Handle MSSQL specific default format.
196
+ def column_schema_normalize_default(default, type)
197
+ if m = MSSQL_DEFAULT_RE.match(default)
198
+ default = m[1] || m[2]
199
+ end
200
+ super(default, type)
201
+ end
202
+
161
203
  # Commit the active transaction on the connection, does not commit/release
162
204
  # savepoints.
163
205
  def commit_transaction(conn, opts={})
@@ -421,6 +463,21 @@ module Sequel
421
463
  mutation_method(:disable_insert_output)
422
464
  end
423
465
 
466
+ # There is no function on Microsoft SQL Server that does character length
467
+ # and respects trailing spaces (datalength respects trailing spaces, but
468
+ # counts bytes instead of characters). Use a hack to work around the
469
+ # trailing spaces issue.
470
+ def emulated_function_sql_append(sql, f)
471
+ case f.f
472
+ when :char_length
473
+ literal_append(sql, SQL::Function.new(:len, Sequel.join([f.args.first, 'x'])) - 1)
474
+ when :trim
475
+ literal_append(sql, SQL::Function.new(:ltrim, SQL::Function.new(:rtrim, f.args.first)))
476
+ else
477
+ super
478
+ end
479
+ end
480
+
424
481
  # MSSQL uses the CONTAINS keyword for full text search
425
482
  def full_text_search(cols, terms, opts = {})
426
483
  terms = "\"#{terms.join('" OR "')}\"" if terms.is_a?(Array)
@@ -33,6 +33,7 @@ module Sequel
33
33
  CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
34
34
  COLUMN_DEFINITION_ORDER = [:collate, :null, :default, :unique, :primary_key, :auto_increment, :references]
35
35
  PRIMARY = 'PRIMARY'.freeze
36
+ MYSQL_TIMESTAMP_RE = /\ACURRENT_(?:DATE|TIMESTAMP)?\z/
36
37
 
37
38
  # MySQL's cast rules are restrictive in that you can't just cast to any possible
38
39
  # database type.
@@ -110,8 +111,10 @@ module Sequel
110
111
 
111
112
  # Get version of MySQL server, used for determined capabilities.
112
113
  def server_version
113
- m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
114
- @server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
114
+ @server_version ||= begin
115
+ m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
116
+ (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
117
+ end
115
118
  end
116
119
 
117
120
  # MySQL supports CREATE TABLE IF NOT EXISTS syntax.
@@ -167,18 +170,50 @@ module Sequel
167
170
 
168
171
  private
169
172
 
170
- # Use MySQL specific syntax for rename column, set column type, and
171
- # drop index cases.
172
- def alter_table_sql(table, op)
173
+ # Preprocess the array of operations. If it looks like some operations depend
174
+ # on results of earlier operations and may require reloading the schema to
175
+ # work correctly, split those operations into separate lists, and between each
176
+ # list, remove the cached schema so that the later operations deal with the
177
+ # then current table schema.
178
+ def apply_alter_table(name, ops)
179
+ modified_columns = []
180
+ op_groups = [[]]
181
+ ops.each do |op|
182
+ case op[:op]
183
+ when :add_column, :set_column_type, :set_column_null, :set_column_default
184
+ if modified_columns.include?(op[:name])
185
+ op_groups << []
186
+ else
187
+ modified_columns << op[:name]
188
+ end
189
+ when :rename_column
190
+ if modified_columns.include?(op[:name]) || modified_columns.include?(op[:new_name])
191
+ op_groups << []
192
+ end
193
+ modified_columns << op[:name] unless modified_columns.include?(op[:name])
194
+ modified_columns << op[:new_name] unless modified_columns.include?(op[:new_name])
195
+ end
196
+ op_groups.last << op
197
+ end
198
+
199
+ op_groups.each do |ops|
200
+ next if ops.empty?
201
+ alter_table_sql_list(name, ops).each{|sql| execute_ddl(sql)}
202
+ remove_cached_schema(name)
203
+ end
204
+ end
205
+
206
+ # Use MySQL specific syntax for some alter table operations.
207
+ def alter_table_op_sql(table, op)
173
208
  case op[:op]
174
209
  when :add_column
175
210
  if related = op.delete(:table)
176
- sql = super(table, op)
211
+ sql = super
177
212
  op[:table] = related
178
213
  op[:key] ||= primary_key_from_schema(related)
179
- [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
214
+ sql << ", ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"
180
215
  else
181
- super(table, op)
216
+ super
182
217
  end
183
218
  when :rename_column, :set_column_type, :set_column_null, :set_column_default
184
219
  o = op[:op]
@@ -189,31 +224,52 @@ module Sequel
189
224
  opts[:null] = o == :set_column_null ? op[:null] : opts[:allow_null]
190
225
  opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
191
226
  opts.delete(:default) if opts[:default] == nil
192
- "ALTER TABLE #{quote_schema_table(table)} CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
193
- when :drop_index
194
- "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
227
+ "CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
195
228
  when :drop_constraint
196
229
  type = case op[:type]
197
230
  when :primary_key
198
- return "ALTER TABLE #{quote_schema_table(table)} DROP PRIMARY KEY"
231
+ "DROP PRIMARY KEY"
199
232
  when :foreign_key
200
- 'FOREIGN KEY'
233
+ "DROP FOREIGN KEY #{quote_identifier(op[:name])}"
201
234
  when :unique
202
- 'INDEX'
203
- else
204
- raise(Error, "must specify constraint type via :type=>(:foreign_key|:primary_key|:unique) when dropping constraints on MySQL")
235
+ "DROP INDEX #{quote_identifier(op[:name])}"
205
236
  end
206
- "ALTER TABLE #{quote_schema_table(table)} DROP #{type} #{quote_identifier(op[:name])}"
207
237
  when :add_constraint
208
238
  if op[:type] == :foreign_key
209
239
  op[:key] ||= primary_key_from_schema(op[:table])
210
240
  end
211
- super(table, op)
241
+ super
212
242
  else
213
- super(table, op)
243
+ super
214
244
  end
215
245
  end
216
246
 
247
+ # MySQL server requires table names when dropping indexes.
248
+ def alter_table_sql(table, op)
249
+ case op[:op]
250
+ when :drop_index
251
+ "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
252
+ else
253
+ super
254
+ end
255
+ end
256
+
257
+ # Handle MySQL specific default format.
258
+ def column_schema_normalize_default(default, type)
259
+ if column_schema_default_string_type?(type)
260
+ return if [:date, :datetime, :time].include?(type) && MYSQL_TIMESTAMP_RE.match(default)
261
+ default = "'#{default.gsub("'", "''").gsub('\\', '\\\\')}'"
262
+ end
263
+ super(default, type)
264
+ end
265
+
266
+ # Don't allow combining adding foreign key operations with other
267
+ # operations, since in some cases adding a foreign key constraint in
268
+ # the same query as other operations results in MySQL error 150.
269
+ def combinable_alter_table_op?(op)
270
+ super && !(op[:op] == :add_constraint && op[:type] == :foreign_key)
271
+ end
272
+
217
273
  # The SQL queries to execute on initial connection
218
274
  def mysql_connection_setting_sqls
219
275
  sqls = []
@@ -369,6 +425,16 @@ module Sequel
369
425
  end
370
426
  end
371
427
 
428
+ # Recognize MySQL set type.
429
+ def schema_column_type(db_type)
430
+ case db_type
431
+ when /\Aset/io
432
+ :set
433
+ else
434
+ super
435
+ end
436
+ end
437
+
372
438
  # Use the MySQL specific DESCRIBE syntax to get a table description.
373
439
  def schema_parse_table(table_name, opts)
374
440
  m = output_identifier_meth(opts[:dataset])
@@ -387,6 +453,11 @@ module Sequel
387
453
  end
388
454
  end
389
455
 
456
+ # MySQL can combine multiple alter table ops into a single query.
457
+ def supports_combining_alter_table_ops?
458
+ true
459
+ end
460
+
390
461
  # Respect the :size option if given to produce
391
462
  # tinyblob, mediumblob, and longblob if :tiny,
392
463
  # :medium, or :long is given.
@@ -667,6 +738,11 @@ module Sequel
667
738
  false
668
739
  end
669
740
 
741
+ # MySQL supports pattern matching via regular expressions
742
+ def supports_regexp?
743
+ true
744
+ end
745
+
670
746
  # MySQL does support fractional timestamps in literal timestamps, but it
671
747
  # ignores them. Also, using them seems to cause problems on 1.9. Since
672
748
  # they are ignored anyway, not using them is probably best.