sequel 5.70.0 → 5.80.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 +134 -0
- data/README.rdoc +7 -5
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/opening_databases.rdoc +6 -2
- data/doc/querying.rdoc +6 -1
- 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/schema_modification.rdoc +2 -2
- data/doc/testing.rdoc +4 -2
- 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/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +6 -5
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +30 -2
- data/lib/sequel/adapters/shared/mysql.rb +68 -3
- data/lib/sequel/adapters/shared/oracle.rb +4 -6
- data/lib/sequel/adapters/shared/postgres.rb +116 -6
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -4
- data/lib/sequel/adapters/shared/sqlite.rb +20 -3
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/connection_pool.rb +4 -2
- data/lib/sequel/database/misc.rb +3 -2
- data/lib/sequel/database/schema_methods.rb +11 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +8 -6
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/features.rb +10 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/query.rb +58 -9
- data/lib/sequel/dataset/sql.rb +47 -34
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +7 -0
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/caller_logging.rb +2 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/migration.rb +64 -14
- data/lib/sequel/extensions/named_timezones.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +10 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +4 -4
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/provenance.rb +108 -0
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +9 -2
- data/lib/sequel/model/base.rb +26 -13
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/list.rb +5 -2
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- 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 +5 -1
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +44 -3
@@ -0,0 +1,67 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* SQLite 3.45+ jsonb functions are now supported in the sqlite_json_ops
|
4
|
+
extension. Similar to the postgres_json_ops extension, there are
|
5
|
+
now separate methods for dealing with json and jsonb types:
|
6
|
+
|
7
|
+
Sequel.sqlite_json_op(:column) # json
|
8
|
+
Sequel.sqlite_jsonb_op(:column) # jsonb
|
9
|
+
|
10
|
+
Some methods that use json_* functions for json ops use jsonb_*
|
11
|
+
functions for jsonb ops:
|
12
|
+
|
13
|
+
jb = Sequel.sqlite_jsonb_op(:column)
|
14
|
+
jb.extract('$.a') # jsonb_extract(column, '$.a')
|
15
|
+
jb.insert('$.a', 1) # jsonb_insert(column, '$.a', 1)
|
16
|
+
jb.set('$.a', 1) # jsonb_set(column, '$.a', 1)
|
17
|
+
jb.replace('$.a', 1) # jsonb_replace(column, '$.a', 1)
|
18
|
+
jb.remove('$.a') # jsonb_remove(column, '$.a')
|
19
|
+
jb.patch('{"a":2}') # jsonb_patch(column, '{"a":2}')
|
20
|
+
|
21
|
+
You can use the json and jsonb methods to convert jsonb to json
|
22
|
+
and json to jsonb, respectively.
|
23
|
+
|
24
|
+
jb.json # json(column)
|
25
|
+
|
26
|
+
Use of the json method on jsonb types is important, because if you
|
27
|
+
want to be able to deal with the values in Ruby, you must convert
|
28
|
+
the jsonb value to json in the database before the database returns
|
29
|
+
the value. Unlike PostgreSQL, SQLite will not convert the value
|
30
|
+
from jsonb to json on retrieval, and direct use of SQLite's jsonb
|
31
|
+
format is unsupported by SQLite as it is subject to change.
|
32
|
+
|
33
|
+
* Database#with_advisory_lock is now supported on PostgreSQL, MySQL,
|
34
|
+
and Microsoft SQL Server. This supports advisory (explicit)
|
35
|
+
locking, using the database-specific APIs. To work on all three
|
36
|
+
servers, lock ids should be integers in the signed 64-bit range.
|
37
|
+
|
38
|
+
DB.with_advisory_lock(1234) do
|
39
|
+
# do something
|
40
|
+
end
|
41
|
+
|
42
|
+
By default, an AdvisoryLockError is raised if the lock cannot be
|
43
|
+
immediately acquired. You can use the :wait option to wait until
|
44
|
+
the lock can be acquired, instead of raising.
|
45
|
+
|
46
|
+
DB.with_advisory_lock(1234, wait: true) do
|
47
|
+
# do something
|
48
|
+
end
|
49
|
+
|
50
|
+
* Migrator.run now supports a :use_advisory_lock option to use
|
51
|
+
advisory locks when running migrations, so that it does not
|
52
|
+
attempt to run the same migration more than once in the case
|
53
|
+
where multiple processes are running the migrator simultaneously.
|
54
|
+
It's probably best to avoid running the migrator in multiple
|
55
|
+
processes simultaneously instead of relying on this option.
|
56
|
+
|
57
|
+
= Other Improvements
|
58
|
+
|
59
|
+
* Database#values now supports chaining with compounds on
|
60
|
+
PostgreSQL.
|
61
|
+
|
62
|
+
DB.values([[1, 2]]).union(DB.values([[3, 4]]))
|
63
|
+
# SELECT * FROM (VALUES (1, 2) UNION (VALUES (3, 4))) AS t1
|
64
|
+
|
65
|
+
* The internal hash used to store transaction metadata now uses
|
66
|
+
compare_by_identity, which is faster and avoids potential
|
67
|
+
issues if a driver implements connection object equality.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Dataset#select_prepend has been added for prepending to the
|
4
|
+
currently selected columns:
|
5
|
+
|
6
|
+
DB[:table].select_prepend(:column)
|
7
|
+
# SELECT column, table.*
|
8
|
+
|
9
|
+
As not all databases support "SELECT column, *", select_prepend
|
10
|
+
qualifies wildcard selections to all tables referenced in the
|
11
|
+
query.
|
12
|
+
|
13
|
+
The only reason to use select_prepend is if you want the hashes
|
14
|
+
returned by Sequel to be in a specific order. Otherwise, it is
|
15
|
+
better to use select_append.
|
16
|
+
|
17
|
+
* On PostgreSQL, Sequel now supports an :unlogged_tables_default
|
18
|
+
Database option, which will default created tables to be UNLOGGED.
|
19
|
+
This can be useful to speedup testing in some cases, but it should
|
20
|
+
never be used in cases where data integrity is important.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* On PostgreSQL, Database#create_or_replace_view now supports the
|
25
|
+
:materialized option. This allows for dropping an existing
|
26
|
+
materialized view and creating a new one with the same name
|
27
|
+
(PostgreSQL does not have native support for replacing materialized
|
28
|
+
views).
|
@@ -0,0 +1,40 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A provenance dataset extension has been added. This extension makes
|
4
|
+
SQL queries include a comment describing how the dataset was built.
|
5
|
+
This can make debugging complex cases significantly easier. Here's
|
6
|
+
a simple example:
|
7
|
+
|
8
|
+
DB.extension :provenance
|
9
|
+
|
10
|
+
DB[:table].
|
11
|
+
select(:a).
|
12
|
+
where{b > 10}.
|
13
|
+
order(:c).
|
14
|
+
limit(10)
|
15
|
+
# SQL:
|
16
|
+
# SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
|
17
|
+
# -- Dataset Provenance
|
18
|
+
# -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
|
19
|
+
# -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
|
20
|
+
# -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
|
21
|
+
# -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
|
22
|
+
# -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
|
23
|
+
|
24
|
+
With the above example, it's obvious how the dataset is created, but
|
25
|
+
but in real applications, where datasets can be built from multiple
|
26
|
+
files, seeing where each dataset clone was made can be helpful.
|
27
|
+
|
28
|
+
The source listed will skip locations in the Ruby standard library
|
29
|
+
as well as Sequel itself. Other locations can be skipped by
|
30
|
+
providing a Database :provenance_caller_ignore Regexp option:
|
31
|
+
|
32
|
+
DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
|
33
|
+
|
34
|
+
= Other Improvements
|
35
|
+
|
36
|
+
* For dataset methods where Sequel can determine that the return
|
37
|
+
value would be equivalent to the receiver, Sequel now returns the
|
38
|
+
receiver. This reduces the number of dataset allocations.
|
39
|
+
|
40
|
+
* Sequel now supports Dataset#skip_locked on MariaDB 10.6+.
|
@@ -81,8 +81,8 @@ appropriate 64-bit integer type for the database you are using.
|
|
81
81
|
|
82
82
|
=== Column options
|
83
83
|
|
84
|
-
When using the type name as method, the
|
85
|
-
method, the
|
84
|
+
When using the type name as method, the second argument is an options hash, and when using the +column+
|
85
|
+
method, the third argument is the options hash. The following options are supported:
|
86
86
|
|
87
87
|
:default :: The default value for the column.
|
88
88
|
:index :: Create an index on this column. If given a hash, use the hash as the
|
data/doc/testing.rdoc
CHANGED
@@ -159,12 +159,13 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
|
|
159
159
|
|
160
160
|
=== Other
|
161
161
|
|
162
|
+
SEQUEL_AUTO_CAST_DATE_TIME :: Use the auto_cast_date_and_time extension when running the specs
|
162
163
|
SEQUEL_ASYNC_THREAD_POOL :: Use the async_thread_pool extension when running the specs
|
163
164
|
SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when running the specs, with the :preempt_async_thread option
|
164
165
|
SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
|
165
166
|
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
166
167
|
SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
|
167
|
-
SEQUEL_CONNECTION_VALIDATOR :: Use the
|
168
|
+
SEQUEL_CONNECTION_VALIDATOR :: Use the connection_validator extension when running the adapter/integration specs
|
168
169
|
SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
|
169
170
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
170
171
|
SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
|
@@ -176,7 +177,7 @@ SEQUEL_MODEL_PREPARED_STATEMENTS :: Use the prepared_statements plugin when runn
|
|
176
177
|
SEQUEL_MODEL_THROW_FAILURES :: Use the throw_failures plugin when running the specs
|
177
178
|
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
178
179
|
SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
|
179
|
-
SEQUEL_PG_AUTO_PARAMETERIZE :: Use the pg_auto_parameterize extension when running the postgres specs
|
180
|
+
SEQUEL_PG_AUTO_PARAMETERIZE :: Use the pg_auto_parameterize extension when running the postgres specs. Value can be +in_array+ to test the pg_auto_parameterize_in_array extension, and +in_array_string+ to test the pg_auto_parameterize_in_array extension with the +:treat_in_string_list_as_text_array+ Database option set.
|
180
181
|
SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
|
181
182
|
SEQUEL_PRIMARY_KEY_LOOKUP_CHECK_VALUES :: Use the primary_key_lookup_check_values extension when running the adapter or integration specs
|
182
183
|
SEQUEL_QUERY_PER_ASSOCIATION_DB_0_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
@@ -185,4 +186,5 @@ SEQUEL_QUERY_PER_ASSOCIATION_DB_2_URL :: Run query-per-association integration t
|
|
185
186
|
SEQUEL_QUERY_PER_ASSOCIATION_DB_3_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
186
187
|
SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
|
187
188
|
SEQUEL_SYNCHRONIZE_SQL :: Use the synchronize_sql extension when running the specs
|
189
|
+
SEQUEL_TRANSACTION_CONNECTION_VALIDATOR :: Use the transaction_connection_validator extension when running the adapter/integration specs
|
188
190
|
SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
3
|
Sequel::JDBC.load_driver('org.h2.Driver', :H2)
|
4
|
+
require_relative '../../extensions/auto_cast_date_and_time'
|
4
5
|
|
5
6
|
module Sequel
|
6
7
|
module JDBC
|
@@ -14,6 +15,8 @@ module Sequel
|
|
14
15
|
|
15
16
|
module H2
|
16
17
|
module DatabaseMethods
|
18
|
+
include AutoCastDateAndTime
|
19
|
+
|
17
20
|
def commit_prepared_transaction(transaction_id, opts=OPTS)
|
18
21
|
run("COMMIT TRANSACTION #{transaction_id}", opts)
|
19
22
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
Sequel::JDBC.load_driver('org.hsqldb.jdbcDriver', :HSQLDB)
|
4
4
|
require_relative 'transactions'
|
5
|
+
require_relative '../../extensions/auto_cast_date_and_time'
|
5
6
|
|
6
7
|
module Sequel
|
7
8
|
module JDBC
|
@@ -15,6 +16,7 @@ module Sequel
|
|
15
16
|
|
16
17
|
module HSQLDB
|
17
18
|
module DatabaseMethods
|
19
|
+
include AutoCastDateAndTime
|
18
20
|
include ::Sequel::JDBC::Transactions
|
19
21
|
|
20
22
|
def database_type
|
@@ -199,6 +199,7 @@ module Sequel
|
|
199
199
|
v.strftime("'%H:%M:%S#{sprintf(".%03d", (v.usec/1000.0).round)}'")
|
200
200
|
end
|
201
201
|
|
202
|
+
INTEGER_TYPE = Java::JavaSQL::Types::INTEGER
|
202
203
|
STRING_TYPE = Java::JavaSQL::Types::VARCHAR
|
203
204
|
ARRAY_TYPE = Java::JavaSQL::Types::ARRAY
|
204
205
|
PG_SPECIFIC_TYPES = [Java::JavaSQL::Types::ARRAY, Java::JavaSQL::Types::OTHER, Java::JavaSQL::Types::STRUCT, Java::JavaSQL::Types::TIME_WITH_TIMEZONE, Java::JavaSQL::Types::TIME].freeze
|
@@ -219,6 +220,8 @@ module Sequel
|
|
219
220
|
oid = meta.getField(i).getOID
|
220
221
|
if pr = db.oid_convertor_proc(oid)
|
221
222
|
pr
|
223
|
+
elsif oid == 28 # XID (Transaction ID)
|
224
|
+
map[INTEGER_TYPE]
|
222
225
|
elsif oid == 2950 # UUID
|
223
226
|
map[STRING_TYPE]
|
224
227
|
elsif meta.getPGType(i) == 'hstore'
|
@@ -36,6 +36,10 @@ module Sequel
|
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
|
+
def database_exception_use_sqlstates?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
39
43
|
# Use @@IDENTITY to get the last inserted id
|
40
44
|
def last_insert_id(conn, opts=OPTS)
|
41
45
|
statement(conn) do |stmt|
|
@@ -52,6 +56,17 @@ module Sequel
|
|
52
56
|
|
53
57
|
private
|
54
58
|
|
59
|
+
# JDBC SQLAnywhere driver does not appear to handle fractional
|
60
|
+
# times correctly.
|
61
|
+
def default_time_format
|
62
|
+
"'%H:%M:%S'"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set to zero to work around JDBC SQLAnywhere driver bug.
|
66
|
+
def sqltime_precision
|
67
|
+
0
|
68
|
+
end
|
69
|
+
|
55
70
|
SMALLINT_TYPE = Java::JavaSQL::Types::SMALLINT
|
56
71
|
BOOLEAN_METHOD = Object.new
|
57
72
|
def BOOLEAN_METHOD.call(r, i)
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -396,11 +396,16 @@ module Sequel
|
|
396
396
|
|
397
397
|
def database_exception_sqlstate(exception, opts)
|
398
398
|
if database_exception_use_sqlstates?
|
399
|
-
|
400
|
-
exception = exception.cause
|
401
|
-
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
402
|
-
end
|
399
|
+
_database_exception_sqlstate(exception, opts)
|
403
400
|
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def _database_exception_sqlstate(exception, opts)
|
404
|
+
16.times do
|
405
|
+
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
406
|
+
break unless exception.respond_to?(:cause) && (exception = exception.cause)
|
407
|
+
end
|
408
|
+
|
404
409
|
nil
|
405
410
|
end
|
406
411
|
|
@@ -415,8 +420,7 @@ module Sequel
|
|
415
420
|
|
416
421
|
# Raise a disconnect error if the SQL state of the cause of the exception indicates so.
|
417
422
|
def disconnect_error?(exception, opts)
|
418
|
-
|
419
|
-
super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
|
423
|
+
super || (_database_exception_sqlstate(exception, opts) =~ /^08/)
|
420
424
|
end
|
421
425
|
|
422
426
|
# Execute the prepared statement. If the provided name is a
|
@@ -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
|
@@ -188,8 +188,8 @@ module Sequel
|
|
188
188
|
# :nocov:
|
189
189
|
# Not covered by tests as tests use pg_extended_date_support
|
190
190
|
# extension, which has basically the same code.
|
191
|
-
when
|
192
|
-
|
191
|
+
when Time, DateTime
|
192
|
+
@default_dataset.literal_date_or_time(arg)
|
193
193
|
# :nocov:
|
194
194
|
else
|
195
195
|
arg
|
@@ -672,6 +672,7 @@ module Sequel
|
|
672
672
|
# cursor usage.
|
673
673
|
# :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
|
674
674
|
# numbers result in fewer queries but greater memory use.
|
675
|
+
# :skip_transaction :: Same as :hold, but :hold takes priority.
|
675
676
|
#
|
676
677
|
# Usage:
|
677
678
|
#
|
@@ -764,13 +765,13 @@ module Sequel
|
|
764
765
|
|
765
766
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
766
767
|
def cursor_fetch_rows(sql)
|
767
|
-
server_opts = {:server=>@opts[:server] || :read_only}
|
768
768
|
cursor = @opts[:cursor]
|
769
|
-
hold = cursor[:
|
769
|
+
hold = cursor.fetch(:hold){cursor[:skip_transaction]}
|
770
|
+
server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
|
770
771
|
cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
|
771
772
|
rows_per_fetch = cursor[:rows_per_fetch].to_i
|
772
773
|
|
773
|
-
db.
|
774
|
+
db.transaction(server_opts) do
|
774
775
|
begin
|
775
776
|
execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
|
776
777
|
rows_per_fetch = 1000 if rows_per_fetch <= 0
|
@@ -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
|
@@ -932,7 +960,7 @@ module Sequel
|
|
932
960
|
# since that is the format that is multilanguage and not
|
933
961
|
# DATEFORMAT dependent.
|
934
962
|
def default_timestamp_format
|
935
|
-
"'%Y-%m-%dT%H:%M:%S
|
963
|
+
"'%Y-%m-%dT%H:%M:%S.%3N'"
|
936
964
|
end
|
937
965
|
|
938
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)
|
@@ -646,7 +681,7 @@ module Sequel
|
|
646
681
|
MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
|
647
682
|
|
648
683
|
Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
|
649
|
-
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')
|
650
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')
|
651
686
|
Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
|
652
687
|
|
@@ -774,6 +809,21 @@ module Sequel
|
|
774
809
|
clone(:insert_ignore=>true)
|
775
810
|
end
|
776
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
|
+
|
777
827
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
778
828
|
# If you pass no arguments, ALL fields will be
|
779
829
|
# updated with the new values. If you pass the fields you
|
@@ -871,9 +921,14 @@ module Sequel
|
|
871
921
|
true
|
872
922
|
end
|
873
923
|
|
874
|
-
#
|
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.
|
875
930
|
def supports_skip_locked?
|
876
|
-
|
931
|
+
db.server_version >= (db.mariadb? ? 100600 : 80000)
|
877
932
|
end
|
878
933
|
|
879
934
|
# Check the database setting for whether fractional timestamps
|
@@ -909,6 +964,16 @@ module Sequel
|
|
909
964
|
super if type == :truncate || @opts[:offset]
|
910
965
|
end
|
911
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
|
+
|
912
977
|
# Consider the first table in the joined dataset is the table to delete
|
913
978
|
# from, but include the others for the purposes of selecting rows.
|
914
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
|
@@ -326,6 +327,8 @@ module Sequel
|
|
326
327
|
end
|
327
328
|
|
328
329
|
module DatasetMethods
|
330
|
+
include AutoCastDateAndTime
|
331
|
+
|
329
332
|
ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
|
330
333
|
BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
|
331
334
|
|
@@ -623,7 +626,7 @@ module Sequel
|
|
623
626
|
|
624
627
|
# The strftime format to use when literalizing the time.
|
625
628
|
def default_timestamp_format
|
626
|
-
"
|
629
|
+
"'%Y-%m-%d %H:%M:%S.%6N %:z'"
|
627
630
|
end
|
628
631
|
|
629
632
|
def empty_from_sql
|
@@ -660,11 +663,6 @@ module Sequel
|
|
660
663
|
super
|
661
664
|
end
|
662
665
|
|
663
|
-
# Use a colon for the timestamp offset, since Oracle appears to require it.
|
664
|
-
def format_timestamp_offset(hour, minute)
|
665
|
-
sprintf("%+03i:%02i", hour, minute)
|
666
|
-
end
|
667
|
-
|
668
666
|
# Oracle doesn't support empty values when inserting.
|
669
667
|
def insert_supports_empty_values?
|
670
668
|
false
|