sequel 3.44.0 → 3.45.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/CHANGELOG +44 -0
  2. data/Rakefile +12 -4
  3. data/doc/reflection.rdoc +3 -3
  4. data/doc/release_notes/3.45.0.txt +179 -0
  5. data/doc/schema_modification.rdoc +1 -1
  6. data/doc/transactions.rdoc +23 -0
  7. data/lib/sequel/adapters/db2.rb +1 -0
  8. data/lib/sequel/adapters/ibmdb.rb +19 -3
  9. data/lib/sequel/adapters/jdbc.rb +15 -0
  10. data/lib/sequel/adapters/jdbc/derby.rb +1 -5
  11. data/lib/sequel/adapters/jdbc/h2.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  17. data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
  18. data/lib/sequel/adapters/mysql.rb +4 -0
  19. data/lib/sequel/adapters/mysql2.rb +5 -1
  20. data/lib/sequel/adapters/oracle.rb +18 -0
  21. data/lib/sequel/adapters/postgres.rb +11 -1
  22. data/lib/sequel/adapters/shared/access.rb +14 -2
  23. data/lib/sequel/adapters/shared/cubrid.rb +1 -11
  24. data/lib/sequel/adapters/shared/db2.rb +11 -6
  25. data/lib/sequel/adapters/shared/mssql.rb +10 -10
  26. data/lib/sequel/adapters/shared/mysql.rb +11 -1
  27. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
  28. data/lib/sequel/adapters/shared/oracle.rb +16 -15
  29. data/lib/sequel/adapters/shared/postgres.rb +91 -59
  30. data/lib/sequel/adapters/shared/sqlite.rb +1 -4
  31. data/lib/sequel/adapters/tinytds.rb +15 -0
  32. data/lib/sequel/connection_pool/threaded.rb +1 -1
  33. data/lib/sequel/core.rb +10 -0
  34. data/lib/sequel/database/connecting.rb +2 -0
  35. data/lib/sequel/database/misc.rb +46 -4
  36. data/lib/sequel/database/query.rb +33 -14
  37. data/lib/sequel/database/schema_methods.rb +0 -5
  38. data/lib/sequel/dataset/misc.rb +9 -0
  39. data/lib/sequel/dataset/mutation.rb +9 -7
  40. data/lib/sequel/dataset/sql.rb +13 -0
  41. data/lib/sequel/exceptions.rb +3 -0
  42. data/lib/sequel/extensions/connection_validator.rb +1 -1
  43. data/lib/sequel/extensions/date_arithmetic.rb +0 -8
  44. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  45. data/lib/sequel/extensions/named_timezones.rb +18 -2
  46. data/lib/sequel/extensions/pg_array.rb +5 -1
  47. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  48. data/lib/sequel/extensions/pg_hstore.rb +2 -0
  49. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  50. data/lib/sequel/extensions/pg_json.rb +3 -1
  51. data/lib/sequel/extensions/pg_range.rb +2 -0
  52. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  53. data/lib/sequel/extensions/pg_row.rb +2 -0
  54. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  55. data/lib/sequel/extensions/query.rb +18 -22
  56. data/lib/sequel/model/associations.rb +3 -4
  57. data/lib/sequel/model/base.rb +2 -0
  58. data/lib/sequel/plugins/force_encoding.rb +2 -0
  59. data/lib/sequel/plugins/json_serializer.rb +155 -39
  60. data/lib/sequel/plugins/serialization.rb +14 -2
  61. data/lib/sequel/plugins/unlimited_update.rb +31 -0
  62. data/lib/sequel/plugins/validation_class_methods.rb +6 -4
  63. data/lib/sequel/plugins/xml_serializer.rb +133 -30
  64. data/lib/sequel/sql.rb +2 -0
  65. data/lib/sequel/timezones.rb +4 -0
  66. data/lib/sequel/version.rb +1 -1
  67. data/spec/adapters/mysql_spec.rb +0 -11
  68. data/spec/adapters/postgres_spec.rb +86 -54
  69. data/spec/adapters/spec_helper.rb +6 -0
  70. data/spec/core/connection_pool_spec.rb +16 -0
  71. data/spec/core/database_spec.rb +77 -1
  72. data/spec/core/dataset_spec.rb +30 -15
  73. data/spec/core/expression_filters_spec.rb +55 -13
  74. data/spec/core/mock_adapter_spec.rb +4 -0
  75. data/spec/core/schema_spec.rb +0 -2
  76. data/spec/core/spec_helper.rb +5 -0
  77. data/spec/core_extensions_spec.rb +33 -28
  78. data/spec/extensions/constraint_validations_spec.rb +2 -2
  79. data/spec/extensions/core_refinements_spec.rb +12 -12
  80. data/spec/extensions/json_serializer_spec.rb +137 -31
  81. data/spec/extensions/named_timezones_spec.rb +10 -0
  82. data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
  83. data/spec/extensions/pg_json_spec.rb +14 -0
  84. data/spec/extensions/pg_row_spec.rb +11 -0
  85. data/spec/extensions/pretty_table_spec.rb +2 -2
  86. data/spec/extensions/query_spec.rb +11 -8
  87. data/spec/extensions/serialization_spec.rb +20 -0
  88. data/spec/extensions/spec_helper.rb +8 -2
  89. data/spec/extensions/sql_expr_spec.rb +1 -1
  90. data/spec/extensions/unlimited_update_spec.rb +20 -0
  91. data/spec/extensions/xml_serializer_spec.rb +68 -16
  92. data/spec/integration/dataset_test.rb +28 -0
  93. data/spec/integration/spec_helper.rb +6 -0
  94. data/spec/integration/transaction_test.rb +39 -0
  95. data/spec/model/model_spec.rb +1 -1
  96. data/spec/sequel_coverage.rb +15 -0
  97. metadata +8 -3
@@ -10,6 +10,11 @@ module Sequel
10
10
 
11
11
  private
12
12
 
13
+ # JTDS exception handling with SQLState is less accurate than with regexps.
14
+ def database_exception_use_sqlstates?
15
+ false
16
+ end
17
+
13
18
  # Handle nil values by using setNull with the correct parameter type.
14
19
  def set_ps_arg_nil(cps, i)
15
20
  cps.setNull(i, cps.getParameterMetaData.getParameterType(i))
@@ -20,6 +20,11 @@ module Sequel
20
20
  (m = /\/(.*)/.match(u.path)) && m[1]
21
21
  end
22
22
 
23
+ # MySQL exception handling with SQLState is less accurate than with regexps.
24
+ def database_exception_use_sqlstates?
25
+ false
26
+ end
27
+
23
28
  # Get the last inserted id using LAST_INSERT_ID().
24
29
  def last_insert_id(conn, opts={})
25
30
  if stmt = opts[:stmt]
@@ -21,6 +21,11 @@ module Sequel
21
21
 
22
22
  private
23
23
 
24
+ # Oracle exception handling with SQLState is less accurate than with regexps.
25
+ def database_exception_use_sqlstates?
26
+ false
27
+ end
28
+
24
29
  def last_insert_id(conn, opts)
25
30
  unless sequence = opts[:sequence]
26
31
  if t = opts[:table]
@@ -75,12 +80,13 @@ module Sequel
75
80
  private
76
81
 
77
82
  JAVA_BIG_DECIMAL = ::Sequel::JDBC::Dataset::JAVA_BIG_DECIMAL
83
+ JAVA_BIG_DECIMAL_CONSTRUCTOR = java.math.BigDecimal.java_class.constructor(Java::long).method(:new_instance)
78
84
 
79
85
  class ::Sequel::JDBC::Dataset::TYPE_TRANSLATOR
80
86
  def oracle_decimal(v)
81
87
  if v.scale == 0
82
88
  i = v.long_value
83
- if v.equals(JAVA_BIG_DECIMAL.new(i))
89
+ if v.equals(JAVA_BIG_DECIMAL_CONSTRUCTOR.call(i))
84
90
  i
85
91
  else
86
92
  decimal(v)
@@ -30,7 +30,7 @@ module Sequel
30
30
 
31
31
  private
32
32
 
33
- DATABASE_ERROR_REGEXPS = {/Abort due to constraint violation/ => ConstraintViolation}.freeze
33
+ DATABASE_ERROR_REGEXPS = Sequel::SQLite::DatabaseMethods::DATABASE_ERROR_REGEXPS.merge(/Abort due to constraint violation/ => ConstraintViolation).freeze
34
34
  def database_error_regexps
35
35
  DATABASE_ERROR_REGEXPS
36
36
  end
@@ -11,11 +11,33 @@ module Sequel
11
11
  # Check the JDBC DatabaseMetaData for savepoint support
12
12
  def supports_savepoints?
13
13
  return @supports_savepoints if defined?(@supports_savepoints)
14
- @supports_savepoints = synchronize{|c| c.get_meta_data.supports_savepoints}
14
+ @supports_savepoints = synchronize{|c| c.getMetaData.supports_savepoints}
15
+ end
16
+
17
+ # Check the JDBC DatabaseMetaData for support for serializable isolation,
18
+ # since that's the value most people will use.
19
+ def supports_transaction_isolation_levels?
20
+ synchronize{|conn| conn.getMetaData.supportsTransactionIsolationLevel(JavaSQL::Connection::TRANSACTION_SERIALIZABLE)}
15
21
  end
16
22
 
17
23
  private
18
24
 
25
+ JDBC_TRANSACTION_ISOLATION_LEVELS = {:uncommitted=>JavaSQL::Connection::TRANSACTION_READ_UNCOMMITTED,
26
+ :committed=>JavaSQL::Connection::TRANSACTION_READ_COMMITTED,
27
+ :repeatable=>JavaSQL::Connection::TRANSACTION_REPEATABLE_READ,
28
+ :serializable=>JavaSQL::Connection::TRANSACTION_SERIALIZABLE}
29
+
30
+ # Set the transaction isolation level on the given connection using
31
+ # the JDBC API.
32
+ def set_transaction_isolation(conn, opts)
33
+ level = opts.fetch(:isolation, transaction_isolation_level)
34
+ if (jdbc_level = JDBC_TRANSACTION_ISOLATION_LEVELS[level]) &&
35
+ conn.getMetaData.supportsTransactionIsolationLevel(jdbc_level)
36
+ _trans(conn)[:original_jdbc_isolation_level] = conn.getTransactionIsolation
37
+ log_yield("Transaction.isolation_level = #{level}"){conn.setTransactionIsolation(jdbc_level)}
38
+ end
39
+ end
40
+
19
41
  # Most JDBC drivers that support savepoints support releasing them.
20
42
  def supports_releasing_savepoints?
21
43
  true
@@ -30,10 +52,12 @@ module Sequel
30
52
  else
31
53
  log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
32
54
  th[:savepoints] = []
55
+ set_transaction_isolation(conn, opts)
33
56
  end
34
57
  th[:savepoint_level] += 1
35
58
  else
36
59
  log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
60
+ set_transaction_isolation(conn, opts)
37
61
  end
38
62
  end
39
63
 
@@ -53,6 +77,9 @@ module Sequel
53
77
 
54
78
  # Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
55
79
  def remove_transaction(conn, committed)
80
+ if jdbc_level = _trans(conn)[:original_jdbc_isolation_level]
81
+ conn.setTransactionIsolation(jdbc_level)
82
+ end
56
83
  if supports_savepoints?
57
84
  sps = _trans(conn)[:savepoints]
58
85
  conn.setAutoCommit(true) if sps.empty?
@@ -261,6 +261,10 @@ module Sequel
261
261
  [Mysql::Error]
262
262
  end
263
263
 
264
+ def database_exception_sqlstate(exception, opts)
265
+ exception.sqlstate
266
+ end
267
+
264
268
  # Raise a disconnect error if the exception message matches the list
265
269
  # of recognized exceptions.
266
270
  def disconnect_error?(e, opts)
@@ -41,7 +41,7 @@ module Sequel
41
41
  def connect(server)
42
42
  opts = server_opts(server)
43
43
  opts[:host] ||= 'localhost'
44
- opts[:username] ||= opts[:user]
44
+ opts[:username] ||= opts.delete(:user)
45
45
  opts[:flags] = ::Mysql2::Client::FOUND_ROWS if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
46
46
  conn = ::Mysql2::Client.new(opts)
47
47
  conn.query_options.merge!(:symbolize_keys=>true, :cache_rows=>false)
@@ -105,6 +105,10 @@ module Sequel
105
105
  [::Mysql2::Error]
106
106
  end
107
107
 
108
+ def database_exception_sqlstate(exception, opts)
109
+ exception.sql_state
110
+ end
111
+
108
112
  # If a connection object is available, try pinging it. Otherwise, if the
109
113
  # error is a Mysql2::Error, check the SQL state and exception message for
110
114
  # disconnects.
@@ -144,6 +144,23 @@ module Sequel
144
144
  [OCIException, RuntimeError]
145
145
  end
146
146
 
147
+ def database_specific_error_class(exception, opts)
148
+ case exception.code
149
+ when 1400, 1407
150
+ NotNullConstraintViolation
151
+ when 1
152
+ UniqueConstraintViolation
153
+ when 2291, 2292
154
+ ForeignKeyConstraintViolation
155
+ when 2290
156
+ CheckConstraintViolation
157
+ when 8177
158
+ SerializationFailure
159
+ else
160
+ super
161
+ end
162
+ end
163
+
147
164
  def execute_prepared_statement(conn, type, name, opts)
148
165
  ps = prepared_statement(name)
149
166
  sql = ps.prepared_sql
@@ -197,6 +214,7 @@ module Sequel
197
214
 
198
215
  def begin_transaction(conn, opts={})
199
216
  log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
217
+ set_transaction_isolation(conn, opts)
200
218
  end
201
219
 
202
220
  def commit_transaction(conn, opts={})
@@ -20,7 +20,11 @@ rescue LoadError => e
20
20
  else
21
21
  # Raise an error if no valid string escaping method can be found.
22
22
  def escape_string(obj)
23
- raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
23
+ if Sequel::Postgres.force_standard_strings
24
+ str.gsub("'", "''")
25
+ else
26
+ raise Sequel::Error, "string escaping not supported with this postgres driver. Try using ruby-pg, ruby-postgres, or postgres-pr."
27
+ end
24
28
  end
25
29
  end
26
30
  end
@@ -440,6 +444,12 @@ module Sequel
440
444
  [PGError]
441
445
  end
442
446
 
447
+ def database_exception_sqlstate(exception, opts)
448
+ if exception.respond_to?(:result) && (result = exception.result)
449
+ result.error_field(::PGresult::PG_DIAG_SQLSTATE)
450
+ end
451
+ end
452
+
443
453
  # Execute the prepared statement with the given name on an available
444
454
  # connection, using the given args. If the connection has not prepared
445
455
  # a statement with the given name yet, prepare it. If the connection
@@ -94,6 +94,7 @@ module Sequel
94
94
  PAREN_OPEN = Dataset::PAREN_OPEN
95
95
  INTO = Dataset::INTO
96
96
  FROM = Dataset::FROM
97
+ SPACE = Dataset::SPACE
97
98
  NOT_EQUAL = ' <> '.freeze
98
99
  OPS = {:'%'=>' Mod '.freeze, :'||'=>' & '.freeze}
99
100
  BOOL_FALSE = '0'.freeze
@@ -125,9 +126,15 @@ module Sequel
125
126
  def complex_expression_sql_append(sql, op, args)
126
127
  case op
127
128
  when :ILIKE
128
- super(sql, :LIKE, args)
129
+ complex_expression_sql_append(sql, :LIKE, args)
129
130
  when :'NOT ILIKE'
130
- super(sql, :'NOT LIKE', args)
131
+ complex_expression_sql_append(sql, :'NOT LIKE', args)
132
+ when :LIKE, :'NOT LIKE'
133
+ sql << PAREN_OPEN
134
+ literal_append(sql, args.at(0))
135
+ sql << SPACE << op.to_s << SPACE
136
+ literal_append(sql, args.at(1))
137
+ sql << PAREN_CLOSE
131
138
  when :'!='
132
139
  sql << PAREN_OPEN
133
140
  literal_append(sql, args.at(0))
@@ -183,6 +190,11 @@ module Sequel
183
190
  end
184
191
  end
185
192
 
193
+ # Access uses [] to escape metacharacters, instead of backslashes.
194
+ def escape_like(string)
195
+ string.gsub(/[\\*#?\[]/){|m| "[#{m}]"}
196
+ end
197
+
186
198
  # Specify a table for a SELECT ... INTO query.
187
199
  def into(table)
188
200
  clone(:into => table)
@@ -130,6 +130,7 @@ module Sequel
130
130
  /Operation would have caused one or more unique constraint violations/ => UniqueConstraintViolation,
131
131
  /The constraint of the foreign key .+ is invalid|Update\/Delete operations are restricted by the foreign key/ => ForeignKeyConstraintViolation,
132
132
  /cannot be made NULL/ => NotNullConstraintViolation,
133
+ /Your transaction .+ has been unilaterally aborted by the system/ => SerializationFailure,
133
134
  }.freeze
134
135
  def database_error_regexps
135
136
  DATABASE_ERROR_REGEXPS
@@ -163,17 +164,6 @@ module Sequel
163
164
  BOOL_FALSE = '0'.freeze
164
165
  BOOL_TRUE = '1'.freeze
165
166
 
166
- def complex_expression_sql_append(sql, op, args)
167
- case op
168
- when :ILIKE
169
- super(sql, :LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1))])
170
- when :"NOT ILIKE"
171
- super(sql, :"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1))])
172
- else
173
- super
174
- end
175
- end
176
-
177
167
  def supports_join_using?
178
168
  false
179
169
  end
@@ -72,6 +72,11 @@ module Sequel
72
72
  indexes
73
73
  end
74
74
 
75
+ # DB2 supports transaction isolation levels.
76
+ def supports_transaction_isolation_levels?
77
+ true
78
+ end
79
+
75
80
  private
76
81
 
77
82
  # Handle DB2 specific alter table operations.
@@ -159,6 +164,7 @@ module Sequel
159
164
  /DB2 SQL Error: (SQLCODE=-530, SQLSTATE=23503|SQLCODE=-532, SQLSTATE=23504)|The insert or update value of the FOREIGN KEY .+ is not equal to any value of the parent key of the parent table|A parent row cannot be deleted because the relationship .+ restricts the deletion/ => ForeignKeyConstraintViolation,
160
165
  /DB2 SQL Error: SQLCODE=-545, SQLSTATE=23513|The requested operation is not allowed because a row does not satisfy the check constraint/ => CheckConstraintViolation,
161
166
  /DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502|Assignment of a NULL value to a NOT NULL column/ => NotNullConstraintViolation,
167
+ /DB2 SQL Error: SQLCODE=-911, SQLSTATE=40001|The current transaction has been rolled back because of a deadlock or timeout/ => SerializationFailure,
162
168
  }.freeze
163
169
  def database_error_regexps
164
170
  DATABASE_ERROR_REGEXPS
@@ -191,6 +197,11 @@ module Sequel
191
197
  (::Sequel::DB2::use_clob_as_blob && db_type.downcase == 'clob') ? :blob : super
192
198
  end
193
199
 
200
+ # SQL to set the transaction isolation level
201
+ def set_transaction_isolation_sql(level)
202
+ "SET CURRENT ISOLATION #{Database::TRANSACTION_ISOLATION_LEVELS[level]}"
203
+ end
204
+
194
205
  # We uses the clob type by default for Files.
195
206
  # Note: if user select to use blob, then insert statement should use
196
207
  # use this for blob value:
@@ -241,14 +252,8 @@ module Sequel
241
252
  end
242
253
  end
243
254
 
244
- # Handle DB2 specific LIKE and bitwise operator support, and
245
- # emulate the extract method, which DB2 doesn't natively support.
246
255
  def complex_expression_sql_append(sql, op, args)
247
256
  case op
248
- when :ILIKE
249
- super(sql, :LIKE, [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
250
- when :"NOT ILIKE"
251
- super(sql, :"NOT LIKE", [SQL::Function.new(:upper, args.at(0)), SQL::Function.new(:upper, args.at(1)) ])
252
257
  when :&, :|, :^
253
258
  # works with db2 v9.5 and after
254
259
  op = BITWISE_METHOD_MAP[op]
@@ -243,6 +243,7 @@ module Sequel
243
243
  /conflicted with the (FOREIGN KEY.*|REFERENCE) constraint/ => ForeignKeyConstraintViolation,
244
244
  /conflicted with the CHECK constraint/ => CheckConstraintViolation,
245
245
  /column does not allow nulls/ => NotNullConstraintViolation,
246
+ /was deadlocked on lock resources with another process and has been chosen as the deadlock victim/ => SerializationFailure,
246
247
  }.freeze
247
248
  def database_error_regexps
248
249
  DATABASE_ERROR_REGEXPS
@@ -309,8 +310,10 @@ module Sequel
309
310
  # The closest MSSQL equivalent of a boolean datatype is the bit type.
310
311
  def schema_column_type(db_type)
311
312
  case db_type
312
- when /\A(bit)\z/io
313
+ when /\A(?:bit)\z/io
313
314
  :boolean
315
+ when /\A(?:(?:small)?money)\z/io
316
+ :decimal
314
317
  else
315
318
  super
316
319
  end
@@ -432,6 +435,8 @@ module Sequel
432
435
  DEFAULT_TIMESTAMP_FORMAT = "'%Y-%m-%dT%H:%M:%S%N%z'".freeze
433
436
  FORMAT_DATE = "'%Y%m%d'".freeze
434
437
 
438
+ Sequel::Dataset.def_mutation_method(:disable_insert_output, :output, :module=>self)
439
+
435
440
  # Allow overriding of the mssql_unicode_strings option at the dataset level.
436
441
  attr_accessor :mssql_unicode_strings
437
442
 
@@ -484,11 +489,11 @@ module Sequel
484
489
  clone(:disable_insert_output=>true)
485
490
  end
486
491
 
487
- # Disable the use of INSERT OUTPUT, modifying the receiver
488
- def disable_insert_output!
489
- mutation_method(:disable_insert_output)
492
+ # MSSQL treats [] as a metacharacter in LIKE expresions.
493
+ def escape_like(string)
494
+ string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
490
495
  end
491
-
496
+
492
497
  # There is no function on Microsoft SQL Server that does character length
493
498
  # and respects trailing spaces (datalength respects trailing spaces, but
494
499
  # counts bytes instead of characters). Use a hack to work around the
@@ -565,11 +570,6 @@ module Sequel
565
570
  clone({:output => output})
566
571
  end
567
572
 
568
- # An output method that modifies the receiver.
569
- def output!(into, values)
570
- mutation_method(:output, into, values)
571
- end
572
-
573
573
  # MSSQL uses [] to quote identifiers. MSSQL does not support
574
574
  # escaping of ], so you cannot use that character in an identifier.
575
575
  def quoted_identifier_append(sql, name)
@@ -64,7 +64,7 @@ module Sequel
64
64
  im = input_identifier_meth
65
65
  ds = metadata_dataset.
66
66
  from(:INFORMATION_SCHEMA__KEY_COLUMN_USAGE).
67
- where(:TABLE_NAME=>im.call(table)).
67
+ where(:TABLE_NAME=>im.call(table), :TABLE_SCHEMA=>Sequel.function(:DATABASE)).
68
68
  exclude(:CONSTRAINT_NAME=>'PRIMARY').
69
69
  exclude(:REFERENCED_TABLE_NAME=>nil).
70
70
  select(:CONSTRAINT_NAME___name, :COLUMN_NAME___column, :REFERENCED_TABLE_NAME___table, :REFERENCED_COLUMN_NAME___key)
@@ -196,6 +196,9 @@ module Sequel
196
196
  opts[:default] = o == :set_column_default ? op[:default] : opts[:ruby_default]
197
197
  opts.delete(:default) if opts[:default] == nil
198
198
  opts.delete(:primary_key)
199
+ unless op[:type] || opts[:type]
200
+ raise Error, "cannot determine database type to use for CHANGE COLUMN operation"
201
+ end
199
202
  "CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
200
203
  when :drop_constraint
201
204
  type = case op[:type]
@@ -354,6 +357,7 @@ module Sequel
354
357
  /Duplicate entry .+ for key/ => UniqueConstraintViolation,
355
358
  /foreign key constraint fails/ => ForeignKeyConstraintViolation,
356
359
  /cannot be null/ => NotNullConstraintViolation,
360
+ /Deadlock found when trying to get lock; try restarting transaction/ => SerializationFailure,
357
361
  }.freeze
358
362
  def database_error_regexps
359
363
  DATABASE_ERROR_REGEXPS
@@ -505,6 +509,8 @@ module Sequel
505
509
  COMMA = Dataset::COMMA
506
510
  LIMIT = Dataset::LIMIT
507
511
  GROUP_BY = Dataset::GROUP_BY
512
+ ESCAPE = Dataset::ESCAPE
513
+ BACKSLASH = Dataset::BACKSLASH
508
514
  REGEXP = 'REGEXP'.freeze
509
515
  LIKE = 'LIKE'.freeze
510
516
  BINARY = 'BINARY '.freeze
@@ -551,6 +557,10 @@ module Sequel
551
557
  sql << SPACE
552
558
  sql << BINARY if [:~, :'!~', :LIKE, :'NOT LIKE'].include?(op)
553
559
  literal_append(sql, args.at(1))
560
+ if [:LIKE, :'NOT LIKE', :ILIKE, :'NOT ILIKE'].include?(op)
561
+ sql << ESCAPE
562
+ literal_append(sql, BACKSLASH)
563
+ end
554
564
  sql << PAREN_CLOSE
555
565
  when :'||'
556
566
  if args.length > 1
@@ -44,6 +44,22 @@ module Sequel
44
44
  conn.prepared_statements = {}
45
45
  end
46
46
 
47
+ # Stupid MySQL doesn't use SQLState error codes correctly, mapping
48
+ # all constraint violations to 23000 even though it recognizes
49
+ # different types.
50
+ def database_specific_error_class(exception, opts)
51
+ case exception.errno
52
+ when 1048
53
+ NotNullConstraintViolation
54
+ when 1062
55
+ UniqueConstraintViolation
56
+ when 1451, 1452
57
+ ForeignKeyConstraintViolation
58
+ else
59
+ super
60
+ end
61
+ end
62
+
47
63
  # Executes a prepared statement on an available connection. If the
48
64
  # prepared statement already exists for the connection and has the same
49
65
  # SQL, reuse it, otherwise, prepare the new statement. Because of the
@@ -65,8 +81,8 @@ module Sequel
65
81
  _execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
66
82
  end
67
83
  end
68
-
69
84
  end
85
+
70
86
  module DatasetMethods
71
87
  include Sequel::Dataset::StoredProcedures
72
88