sequel 5.60.1 → 5.81.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.
- checksums.yaml +4 -4
- data/CHANGELOG +280 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +27 -24
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/code_order.rdoc +5 -3
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -7
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +7 -2
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/release_notes/5.79.0.txt +28 -0
- data/doc/release_notes/5.80.0.txt +40 -0
- data/doc/release_notes/5.81.0.txt +31 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +10 -5
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +82 -3
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +469 -160
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +39 -3
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +12 -5
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +119 -45
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +21 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +4 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +3 -3
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/provenance.rb +110 -0
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +38 -11
- data/lib/sequel/model/base.rb +32 -18
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +28 -7
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/list.rb +5 -2
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +2 -2
- metadata +79 -6
|
@@ -29,6 +29,21 @@ module Sequel
|
|
|
29
29
|
end
|
|
30
30
|
MYSQL_TYPES.freeze
|
|
31
31
|
|
|
32
|
+
RUBY_MYSQL_3 = !Mysql.respond_to?(:init)
|
|
33
|
+
RUBY_MYSQL_4 = RUBY_MYSQL_3 && ::Mysql::VERSION.to_i >= 4
|
|
34
|
+
|
|
35
|
+
if RUBY_MYSQL_3
|
|
36
|
+
class Adapter < ::Mysql
|
|
37
|
+
alias real_connect connect
|
|
38
|
+
alias use_result store_result
|
|
39
|
+
if RUBY_MYSQL_4
|
|
40
|
+
def initialize(**opts)
|
|
41
|
+
super(**opts.merge(:cast=>false))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
32
47
|
class Database < Sequel::Database
|
|
33
48
|
include Sequel::MySQL::DatabaseMethods
|
|
34
49
|
include Sequel::MySQL::MysqlMysql2::DatabaseMethods
|
|
@@ -72,7 +87,7 @@ module Sequel
|
|
|
72
87
|
def connect(server)
|
|
73
88
|
opts = server_opts(server)
|
|
74
89
|
|
|
75
|
-
if
|
|
90
|
+
if !RUBY_MYSQL_3
|
|
76
91
|
conn = Mysql.init
|
|
77
92
|
conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
|
|
78
93
|
conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
|
|
@@ -88,8 +103,8 @@ module Sequel
|
|
|
88
103
|
conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
|
|
89
104
|
end
|
|
90
105
|
else
|
|
91
|
-
# ruby-mysql 3 API
|
|
92
|
-
conn =
|
|
106
|
+
# ruby-mysql 3+ API
|
|
107
|
+
conn = Adapter.new
|
|
93
108
|
# no support for default group
|
|
94
109
|
conn.local_infile = opts[:config_local_infile] if opts.has_key?(:config_local_infile)
|
|
95
110
|
if encoding = opts[:encoding] || opts[:charset]
|
|
@@ -101,10 +116,7 @@ module Sequel
|
|
|
101
116
|
if connect_timeout = opts[:connect_timeout]
|
|
102
117
|
conn.connect_timeout = connect_timeout
|
|
103
118
|
end
|
|
104
|
-
|
|
105
|
-
alias real_connect connect
|
|
106
|
-
alias use_result store_result
|
|
107
|
-
end
|
|
119
|
+
opts[:compress] = false
|
|
108
120
|
end
|
|
109
121
|
|
|
110
122
|
conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
|
|
@@ -43,7 +43,7 @@ module Sequel
|
|
|
43
43
|
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
44
44
|
# some translation, but allow for millisecond precision.
|
|
45
45
|
def default_timestamp_format
|
|
46
|
-
"{ts '%Y-%m-%d %H:%M:%S
|
|
46
|
+
"{ts '%Y-%m-%d %H:%M:%S.%3N'}"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Use ODBC format, not Microsoft format, as the ODBC layer does
|
|
@@ -312,6 +312,7 @@ module Sequel
|
|
|
312
312
|
:char_used => column.char_used?,
|
|
313
313
|
:char_size => column.char_size,
|
|
314
314
|
:data_size => column.data_size,
|
|
315
|
+
:column_size => column.precision,
|
|
315
316
|
:precision => column.precision,
|
|
316
317
|
:scale => column.scale,
|
|
317
318
|
:fsprecision => column.fsprecision,
|
|
@@ -185,8 +185,12 @@ module Sequel
|
|
|
185
185
|
case arg
|
|
186
186
|
when Sequel::SQL::Blob
|
|
187
187
|
{:value=>arg, :type=>17, :format=>1}
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
# :nocov:
|
|
189
|
+
# Not covered by tests as tests use pg_extended_date_support
|
|
190
|
+
# extension, which has basically the same code.
|
|
191
|
+
when Time, DateTime
|
|
192
|
+
@default_dataset.literal_date_or_time(arg)
|
|
193
|
+
# :nocov:
|
|
190
194
|
else
|
|
191
195
|
arg
|
|
192
196
|
end
|
|
@@ -668,6 +672,7 @@ module Sequel
|
|
|
668
672
|
# cursor usage.
|
|
669
673
|
# :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
|
|
670
674
|
# numbers result in fewer queries but greater memory use.
|
|
675
|
+
# :skip_transaction :: Same as :hold, but :hold takes priority.
|
|
671
676
|
#
|
|
672
677
|
# Usage:
|
|
673
678
|
#
|
|
@@ -760,13 +765,13 @@ module Sequel
|
|
|
760
765
|
|
|
761
766
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
|
762
767
|
def cursor_fetch_rows(sql)
|
|
763
|
-
server_opts = {:server=>@opts[:server] || :read_only}
|
|
764
768
|
cursor = @opts[:cursor]
|
|
765
|
-
hold = cursor[:
|
|
769
|
+
hold = cursor.fetch(:hold){cursor[:skip_transaction]}
|
|
770
|
+
server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
|
|
766
771
|
cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
|
|
767
772
|
rows_per_fetch = cursor[:rows_per_fetch].to_i
|
|
768
773
|
|
|
769
|
-
db.
|
|
774
|
+
db.transaction(server_opts) do
|
|
770
775
|
begin
|
|
771
776
|
execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
|
|
772
777
|
rows_per_fetch = 1000 if rows_per_fetch <= 0
|
|
@@ -17,7 +17,7 @@ module Sequel
|
|
|
17
17
|
|
|
18
18
|
# Doesn't work, due to security restrictions on MSysObjects
|
|
19
19
|
#def tables
|
|
20
|
-
# from(:MSysObjects).where(:
|
|
20
|
+
# from(:MSysObjects).where(Type: 1, Flags: 0).select_map(:Name).map(&:to_sym)
|
|
21
21
|
#end
|
|
22
22
|
|
|
23
23
|
# Access doesn't support renaming tables from an SQL query,
|
|
@@ -57,6 +57,14 @@ module Sequel
|
|
|
57
57
|
DATABASE_ERROR_REGEXPS
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
# Access's Byte type will accept much larger values,
|
|
61
|
+
# even though it only stores 0-255. Do not set min/max
|
|
62
|
+
# values for the Byte type.
|
|
63
|
+
def column_schema_integer_min_max_values(column)
|
|
64
|
+
return if /byte/i =~ column[:db_type]
|
|
65
|
+
super
|
|
66
|
+
end
|
|
67
|
+
|
|
60
68
|
def drop_index_sql(table, op)
|
|
61
69
|
"DROP INDEX #{quote_identifier(op[:name] || default_index_name(table, op[:columns]))} ON #{quote_schema_table(table)}"
|
|
62
70
|
end
|
|
@@ -215,6 +215,18 @@ module Sequel
|
|
|
215
215
|
DATABASE_ERROR_REGEXPS
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
+
DISCONNECT_SQL_STATES = %w'40003 08001 08003'.freeze
|
|
219
|
+
def disconnect_error?(exception, opts)
|
|
220
|
+
sqlstate = database_exception_sqlstate(exception, opts)
|
|
221
|
+
|
|
222
|
+
case sqlstate
|
|
223
|
+
when *DISCONNECT_SQL_STATES
|
|
224
|
+
true
|
|
225
|
+
else
|
|
226
|
+
super
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
218
230
|
# DB2 has issues with quoted identifiers, so
|
|
219
231
|
# turn off database quoting by default.
|
|
220
232
|
def quote_identifiers_default
|
|
@@ -32,7 +32,7 @@ module Sequel
|
|
|
32
32
|
#
|
|
33
33
|
# Options:
|
|
34
34
|
# :args :: Arguments to stored procedure. For named arguments, this should be a
|
|
35
|
-
# hash keyed by argument
|
|
35
|
+
# hash keyed by argument name. For unnamed arguments, this should be an
|
|
36
36
|
# array. Output parameters to the function are specified using :output.
|
|
37
37
|
# You can also name output parameters and provide a type by using an
|
|
38
38
|
# array containing :output, the type name, and the parameter name.
|
|
@@ -246,6 +246,34 @@ module Sequel
|
|
|
246
246
|
information_schema_tables('VIEW', opts)
|
|
247
247
|
end
|
|
248
248
|
|
|
249
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will
|
|
250
|
+
# be converted to a string). If successful, yield to the block, then release the advisory lock
|
|
251
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
|
252
|
+
#
|
|
253
|
+
# Options:
|
|
254
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
|
255
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
|
256
|
+
lock_id = lock_id.to_s
|
|
257
|
+
timeout = opts[:wait] ? -1 : 0
|
|
258
|
+
server = opts[:server]
|
|
259
|
+
|
|
260
|
+
synchronize(server) do
|
|
261
|
+
begin
|
|
262
|
+
res = call_mssql_sproc(:sp_getapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockTimeout'=>timeout, 'LockMode'=>'Exclusive', 'LockOwner'=>'Session'})
|
|
263
|
+
|
|
264
|
+
unless locked = res[:result] >= 0
|
|
265
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
yield
|
|
269
|
+
ensure
|
|
270
|
+
if locked
|
|
271
|
+
call_mssql_sproc(:sp_releaseapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockOwner'=>'Session'})
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
249
277
|
private
|
|
250
278
|
|
|
251
279
|
# Add CLUSTERED or NONCLUSTERED as needed
|
|
@@ -325,6 +353,11 @@ module Sequel
|
|
|
325
353
|
false
|
|
326
354
|
end
|
|
327
355
|
|
|
356
|
+
# MSSQL tinyint types are unsigned.
|
|
357
|
+
def column_schema_tinyint_type_is_unsigned?
|
|
358
|
+
true
|
|
359
|
+
end
|
|
360
|
+
|
|
328
361
|
# Handle MSSQL specific default format.
|
|
329
362
|
def column_schema_normalize_default(default, type)
|
|
330
363
|
if m = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/.match(default)
|
|
@@ -399,10 +432,15 @@ module Sequel
|
|
|
399
432
|
# Backbone of the tables and views support.
|
|
400
433
|
def information_schema_tables(type, opts)
|
|
401
434
|
m = output_identifier_meth
|
|
402
|
-
|
|
435
|
+
schema = opts[:schema]||'dbo'
|
|
436
|
+
tables = metadata_dataset.from(Sequel[:information_schema][:tables].as(:t)).
|
|
403
437
|
select(:table_name).
|
|
404
|
-
where(:table_type=>type, :table_schema=>
|
|
438
|
+
where(:table_type=>type, :table_schema=>schema.to_s).
|
|
405
439
|
map{|x| m.call(x[:table_name])}
|
|
440
|
+
|
|
441
|
+
tables.map!{|t| Sequel.qualify(m.call(schema).to_s, m.call(t).to_s)} if opts[:qualify]
|
|
442
|
+
|
|
443
|
+
tables
|
|
406
444
|
end
|
|
407
445
|
|
|
408
446
|
# Always quote identifiers in the metadata_dataset, so schema parsing works.
|
|
@@ -586,6 +624,18 @@ module Sequel
|
|
|
586
624
|
end
|
|
587
625
|
end
|
|
588
626
|
|
|
627
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
|
628
|
+
# cannot wrap it in a subquery. Load entire query in this case to get
|
|
629
|
+
# the number of rows. In general, you should avoid calling this method
|
|
630
|
+
# on datasets with custom SQL.
|
|
631
|
+
def count(*a, &block)
|
|
632
|
+
if (@opts[:sql] && a.empty? && !block)
|
|
633
|
+
naked.to_a.length
|
|
634
|
+
else
|
|
635
|
+
super
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
|
|
589
639
|
# Uses CROSS APPLY to join the given table into the current dataset.
|
|
590
640
|
def cross_apply(table)
|
|
591
641
|
join_table(:cross_apply, table)
|
|
@@ -596,6 +646,19 @@ module Sequel
|
|
|
596
646
|
clone(:disable_insert_output=>true)
|
|
597
647
|
end
|
|
598
648
|
|
|
649
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
|
650
|
+
# cannot wrap it in a subquery. Run query, and if it returns any
|
|
651
|
+
# records, return true. In general, you should avoid calling this method
|
|
652
|
+
# on datasets with custom SQL.
|
|
653
|
+
def empty?
|
|
654
|
+
if @opts[:sql]
|
|
655
|
+
naked.each{return false}
|
|
656
|
+
true
|
|
657
|
+
else
|
|
658
|
+
super
|
|
659
|
+
end
|
|
660
|
+
end
|
|
661
|
+
|
|
599
662
|
# MSSQL treats [] as a metacharacter in LIKE expresions.
|
|
600
663
|
def escape_like(string)
|
|
601
664
|
string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
|
|
@@ -800,11 +863,10 @@ module Sequel
|
|
|
800
863
|
if opts[:return] == :primary_key && !@opts[:output]
|
|
801
864
|
output(nil, [SQL::QualifiedIdentifier.new(:inserted, first_primary_key)])._import(columns, values, opts)
|
|
802
865
|
elsif @opts[:output]
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
866
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
|
867
|
+
# that there's only ever a single statement.
|
|
868
|
+
sql = multi_insert_sql(columns, values)[0]
|
|
869
|
+
naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v}
|
|
808
870
|
else
|
|
809
871
|
super
|
|
810
872
|
end
|
|
@@ -898,7 +960,7 @@ module Sequel
|
|
|
898
960
|
# since that is the format that is multilanguage and not
|
|
899
961
|
# DATEFORMAT dependent.
|
|
900
962
|
def default_timestamp_format
|
|
901
|
-
"'%Y-%m-%dT%H:%M:%S
|
|
963
|
+
"'%Y-%m-%dT%H:%M:%S.%3N'"
|
|
902
964
|
end
|
|
903
965
|
|
|
904
966
|
# Only include the primary table in the main delete clause
|
|
@@ -197,6 +197,41 @@ module Sequel
|
|
|
197
197
|
renames.each{|from,| remove_cached_schema(from)}
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will be
|
|
201
|
+
# converted to a string). If successful, yield to the block, then release the advisory lock
|
|
202
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
|
203
|
+
#
|
|
204
|
+
# DB.with_advisory_lock(1357){DB.get(1)}
|
|
205
|
+
# # SELECT GET_LOCK('1357', 0) LIMIT 1
|
|
206
|
+
# # SELECT 1 AS v LIMIT 1
|
|
207
|
+
# # SELECT RELEASE_LOCK('1357') LIMIT 1
|
|
208
|
+
#
|
|
209
|
+
# Options:
|
|
210
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
|
211
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
|
212
|
+
lock_id = lock_id.to_s
|
|
213
|
+
ds = dataset
|
|
214
|
+
if server = opts[:server]
|
|
215
|
+
ds = ds.server(server)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# MariaDB doesn't support negative values for infinite wait. A wait of 34 years
|
|
219
|
+
# should be reasonably similar to infinity for this case.
|
|
220
|
+
timeout = opts[:wait] ? 1073741823 : 0
|
|
221
|
+
|
|
222
|
+
synchronize(server) do |c|
|
|
223
|
+
begin
|
|
224
|
+
unless locked = ds.get{GET_LOCK(lock_id, timeout)} == 1
|
|
225
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
yield
|
|
229
|
+
ensure
|
|
230
|
+
ds.get{RELEASE_LOCK(lock_id)} if locked
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
200
235
|
private
|
|
201
236
|
|
|
202
237
|
def alter_table_add_column_sql(table, op)
|
|
@@ -550,6 +585,20 @@ module Sequel
|
|
|
550
585
|
end
|
|
551
586
|
end
|
|
552
587
|
|
|
588
|
+
# Return nil if CHECK constraints are not supported, because
|
|
589
|
+
# versions that don't support check constraints don't raise
|
|
590
|
+
# errors for values outside of range.
|
|
591
|
+
def column_schema_integer_min_max_values(column)
|
|
592
|
+
super if supports_check_constraints?
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# Return nil if CHECK constraints are not supported, because
|
|
596
|
+
# versions that don't support check constraints don't raise
|
|
597
|
+
# errors for values outside of range.
|
|
598
|
+
def column_schema_decimal_min_max_values(column)
|
|
599
|
+
super if supports_check_constraints?
|
|
600
|
+
end
|
|
601
|
+
|
|
553
602
|
# Split DROP INDEX ops on MySQL 5.6+, as dropping them in the same
|
|
554
603
|
# statement as dropping a related foreign key causes an error.
|
|
555
604
|
def split_alter_table_op?(op)
|
|
@@ -632,7 +681,7 @@ module Sequel
|
|
|
632
681
|
MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
|
|
633
682
|
|
|
634
683
|
Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
|
|
635
|
-
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
|
|
684
|
+
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update returning')
|
|
636
685
|
Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having window compounds order limit lock')
|
|
637
686
|
Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
|
|
638
687
|
|
|
@@ -760,6 +809,21 @@ module Sequel
|
|
|
760
809
|
clone(:insert_ignore=>true)
|
|
761
810
|
end
|
|
762
811
|
|
|
812
|
+
# Support insert select for associations, so that the model code can use
|
|
813
|
+
# returning instead of a separate query.
|
|
814
|
+
def insert_select(*values)
|
|
815
|
+
return unless supports_insert_select?
|
|
816
|
+
# Handle case where query does not return a row
|
|
817
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
|
821
|
+
# unless the RETURNING clause is already present.
|
|
822
|
+
def insert_select_sql(*values)
|
|
823
|
+
ds = opts[:returning] ? self : returning
|
|
824
|
+
ds.insert_sql(*values)
|
|
825
|
+
end
|
|
826
|
+
|
|
763
827
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
|
764
828
|
# If you pass no arguments, ALL fields will be
|
|
765
829
|
# updated with the new values. If you pass the fields you
|
|
@@ -857,9 +921,14 @@ module Sequel
|
|
|
857
921
|
true
|
|
858
922
|
end
|
|
859
923
|
|
|
860
|
-
#
|
|
924
|
+
# MariaDB 10.5.0 supports INSERT RETURNING.
|
|
925
|
+
def supports_returning?(type)
|
|
926
|
+
(type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
# MySQL 8+ and MariaDB 10.6+ support SKIP LOCKED.
|
|
861
930
|
def supports_skip_locked?
|
|
862
|
-
|
|
931
|
+
db.server_version >= (db.mariadb? ? 100600 : 80000)
|
|
863
932
|
end
|
|
864
933
|
|
|
865
934
|
# Check the database setting for whether fractional timestamps
|
|
@@ -895,6 +964,16 @@ module Sequel
|
|
|
895
964
|
super if type == :truncate || @opts[:offset]
|
|
896
965
|
end
|
|
897
966
|
|
|
967
|
+
# The strftime format to use when literalizing time (Sequel::SQLTime) values.
|
|
968
|
+
def default_time_format
|
|
969
|
+
db.supports_timestamp_usecs? ? super : "'%H:%M:%S'"
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
# The strftime format to use when literalizing timestamp (Time/DateTime) values.
|
|
973
|
+
def default_timestamp_format
|
|
974
|
+
db.supports_timestamp_usecs? ? super : "'%Y-%m-%d %H:%M:%S'"
|
|
975
|
+
end
|
|
976
|
+
|
|
898
977
|
# Consider the first table in the joined dataset is the table to delete
|
|
899
978
|
# from, but include the others for the purposes of selecting rows.
|
|
900
979
|
def delete_from_sql(sql)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../utils/emulate_offset_with_row_number'
|
|
4
|
+
require_relative '../../extensions/auto_cast_date_and_time'
|
|
4
5
|
|
|
5
6
|
module Sequel
|
|
6
7
|
module Oracle
|
|
@@ -178,6 +179,13 @@ module Sequel
|
|
|
178
179
|
''
|
|
179
180
|
end
|
|
180
181
|
|
|
182
|
+
# Support min/max integer values on Oracle only if
|
|
183
|
+
# they use a NUMBER column with a fixed precision
|
|
184
|
+
# and no scale.
|
|
185
|
+
def column_schema_integer_min_max_values(column)
|
|
186
|
+
super if column[:db_type] =~ /NUMBER\(\d+\)/i || (column[:db_type] == 'NUMBER' && column[:column_size].is_a?(Integer) && column[:scale] == 0)
|
|
187
|
+
end
|
|
188
|
+
|
|
181
189
|
def create_sequence_sql(name, opts=OPTS)
|
|
182
190
|
"CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
|
|
183
191
|
end
|
|
@@ -319,6 +327,8 @@ module Sequel
|
|
|
319
327
|
end
|
|
320
328
|
|
|
321
329
|
module DatasetMethods
|
|
330
|
+
include AutoCastDateAndTime
|
|
331
|
+
|
|
322
332
|
ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
|
|
323
333
|
BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
|
|
324
334
|
|
|
@@ -378,7 +388,12 @@ module Sequel
|
|
|
378
388
|
# Use a custom expression with EXISTS to determine whether a dataset
|
|
379
389
|
# is empty.
|
|
380
390
|
def empty?
|
|
381
|
-
|
|
391
|
+
if @opts[:sql]
|
|
392
|
+
naked.each{return false}
|
|
393
|
+
true
|
|
394
|
+
else
|
|
395
|
+
db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
|
|
396
|
+
end
|
|
382
397
|
end
|
|
383
398
|
|
|
384
399
|
# Oracle requires SQL standard datetimes
|
|
@@ -611,7 +626,7 @@ module Sequel
|
|
|
611
626
|
|
|
612
627
|
# The strftime format to use when literalizing the time.
|
|
613
628
|
def default_timestamp_format
|
|
614
|
-
"
|
|
629
|
+
"'%Y-%m-%d %H:%M:%S.%6N %:z'"
|
|
615
630
|
end
|
|
616
631
|
|
|
617
632
|
def empty_from_sql
|
|
@@ -648,11 +663,6 @@ module Sequel
|
|
|
648
663
|
super
|
|
649
664
|
end
|
|
650
665
|
|
|
651
|
-
# Use a colon for the timestamp offset, since Oracle appears to require it.
|
|
652
|
-
def format_timestamp_offset(hour, minute)
|
|
653
|
-
sprintf("%+03i:%02i", hour, minute)
|
|
654
|
-
end
|
|
655
|
-
|
|
656
666
|
# Oracle doesn't support empty values when inserting.
|
|
657
667
|
def insert_supports_empty_values?
|
|
658
668
|
false
|