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.
- 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
|
|