sequel 3.44.0 → 3.45.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 (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