sequel 3.44.0 → 3.45.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -0
- data/Rakefile +12 -4
- data/doc/reflection.rdoc +3 -3
- data/doc/release_notes/3.45.0.txt +179 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/transactions.rdoc +23 -0
- data/lib/sequel/adapters/db2.rb +1 -0
- data/lib/sequel/adapters/ibmdb.rb +19 -3
- data/lib/sequel/adapters/jdbc.rb +15 -0
- data/lib/sequel/adapters/jdbc/derby.rb +1 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
- data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
- data/lib/sequel/adapters/mysql.rb +4 -0
- data/lib/sequel/adapters/mysql2.rb +5 -1
- data/lib/sequel/adapters/oracle.rb +18 -0
- data/lib/sequel/adapters/postgres.rb +11 -1
- data/lib/sequel/adapters/shared/access.rb +14 -2
- data/lib/sequel/adapters/shared/cubrid.rb +1 -11
- data/lib/sequel/adapters/shared/db2.rb +11 -6
- data/lib/sequel/adapters/shared/mssql.rb +10 -10
- data/lib/sequel/adapters/shared/mysql.rb +11 -1
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
- data/lib/sequel/adapters/shared/oracle.rb +16 -15
- data/lib/sequel/adapters/shared/postgres.rb +91 -59
- data/lib/sequel/adapters/shared/sqlite.rb +1 -4
- data/lib/sequel/adapters/tinytds.rb +15 -0
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +10 -0
- data/lib/sequel/database/connecting.rb +2 -0
- data/lib/sequel/database/misc.rb +46 -4
- data/lib/sequel/database/query.rb +33 -14
- data/lib/sequel/database/schema_methods.rb +0 -5
- data/lib/sequel/dataset/misc.rb +9 -0
- data/lib/sequel/dataset/mutation.rb +9 -7
- data/lib/sequel/dataset/sql.rb +13 -0
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +0 -8
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/named_timezones.rb +18 -2
- data/lib/sequel/extensions/pg_array.rb +5 -1
- data/lib/sequel/extensions/pg_array_ops.rb +2 -0
- data/lib/sequel/extensions/pg_hstore.rb +2 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_json.rb +3 -1
- data/lib/sequel/extensions/pg_range.rb +2 -0
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +2 -0
- data/lib/sequel/extensions/pg_row_ops.rb +2 -0
- data/lib/sequel/extensions/query.rb +18 -22
- data/lib/sequel/model/associations.rb +3 -4
- data/lib/sequel/model/base.rb +2 -0
- data/lib/sequel/plugins/force_encoding.rb +2 -0
- data/lib/sequel/plugins/json_serializer.rb +155 -39
- data/lib/sequel/plugins/serialization.rb +14 -2
- data/lib/sequel/plugins/unlimited_update.rb +31 -0
- data/lib/sequel/plugins/validation_class_methods.rb +6 -4
- data/lib/sequel/plugins/xml_serializer.rb +133 -30
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/timezones.rb +4 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +0 -11
- data/spec/adapters/postgres_spec.rb +86 -54
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +77 -1
- data/spec/core/dataset_spec.rb +30 -15
- data/spec/core/expression_filters_spec.rb +55 -13
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/schema_spec.rb +0 -2
- data/spec/core/spec_helper.rb +5 -0
- data/spec/core_extensions_spec.rb +33 -28
- data/spec/extensions/constraint_validations_spec.rb +2 -2
- data/spec/extensions/core_refinements_spec.rb +12 -12
- data/spec/extensions/json_serializer_spec.rb +137 -31
- data/spec/extensions/named_timezones_spec.rb +10 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
- data/spec/extensions/pg_json_spec.rb +14 -0
- data/spec/extensions/pg_row_spec.rb +11 -0
- data/spec/extensions/pretty_table_spec.rb +2 -2
- data/spec/extensions/query_spec.rb +11 -8
- data/spec/extensions/serialization_spec.rb +20 -0
- data/spec/extensions/spec_helper.rb +8 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +20 -0
- data/spec/extensions/xml_serializer_spec.rb +68 -16
- data/spec/integration/dataset_test.rb +28 -0
- data/spec/integration/spec_helper.rb +6 -0
- data/spec/integration/transaction_test.rb +39 -0
- data/spec/model/model_spec.rb +1 -1
- data/spec/sequel_coverage.rb +15 -0
- 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(
|
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 =
|
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.
|
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
|
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
|
-
|
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
|
-
|
129
|
+
complex_expression_sql_append(sql, :LIKE, args)
|
129
130
|
when :'NOT ILIKE'
|
130
|
-
|
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
|
-
#
|
488
|
-
def
|
489
|
-
|
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
|
|