sequel 5.76.0 → 5.78.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 +30 -0
- data/doc/release_notes/5.76.0.txt +1 -1
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +2 -2
- data/doc/testing.rdoc +2 -1
- data/lib/sequel/adapters/shared/mssql.rb +29 -1
- data/lib/sequel/adapters/shared/mysql.rb +35 -0
- data/lib/sequel/adapters/shared/postgres.rb +36 -1
- data/lib/sequel/adapters/shared/sqlite.rb +15 -2
- data/lib/sequel/adapters/sqlite.rb +21 -3
- data/lib/sequel/connection_pool.rb +4 -2
- data/lib/sequel/database/misc.rb +3 -2
- data/lib/sequel/database/schema_methods.rb +6 -0
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/async_thread_pool.rb +7 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/migration.rb +12 -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 +5 -2
- data/lib/sequel/plugins/list.rb +5 -2
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d821d7a1270874ced864c3a4bc66b519b3b5e3c050d2792e8dfc907a677e36a
|
4
|
+
data.tar.gz: 67390870d998d266a1a07d44e72dc85e357226e7f5dd215ca695ab9037840638
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50b737d3a944ec7d1df62dce2df68b6558e912c0d333d0cdf1d32bccd18ecb5dad8439e73f8c5497b4e627ea8caee34f131dee5a57217a479acfab95287f306d
|
7
|
+
data.tar.gz: 3b9631235606432b44cc57aa624c6509dd14833fdc0e5e900945232c3593904a249dbe3106aef1e4e863cf939517cb1abbd16ca179eec6e5b1de90b815de941f
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
=== 5.78.0 (2024-03-01)
|
2
|
+
|
3
|
+
* Support SQLite 3.45+ jsonb functions in the sqlite_json_ops extension (jeremyevans) (#2133)
|
4
|
+
|
5
|
+
* Support compounds (e.g. UNION) in conjunction with Database#values on PostgreSQL (jeremyevans) (#2137)
|
6
|
+
|
7
|
+
* Support :use_advisory_lock option to Migrator.run to use advisory locks when running migrations (jeremyevans) (#2089)
|
8
|
+
|
9
|
+
* Support Database#with_advisory_lock on PostgreSQL, MySQL, and Microsoft SQL Server (jeremyevans) (#2089)
|
10
|
+
|
11
|
+
=== 5.77.0 (2024-02-01)
|
12
|
+
|
13
|
+
* Support create_table :without_rowid option on SQLite (loranger32) (#2126)
|
14
|
+
|
15
|
+
* Warn by default if trying to eager_graph/association_join an association that uses a block, when the block would be ignored (jeremyevans)
|
16
|
+
|
17
|
+
* Speed up validates_unique in validation_helpers plugin by using empty? instead of count == 0 (numbata) (#2122)
|
18
|
+
|
19
|
+
* Speed up regexp matches in sqlite adapter on Ruby 2.4+ (jeremyevans)
|
20
|
+
|
21
|
+
* Add sqlite adapter :regexp_function_cache option for specifying the cache object to use (paddor, jeremyevans) (#2116)
|
22
|
+
|
23
|
+
* Respect list plugin :top option when inserting the first row into the model's table (johanmagnusson) (#2115)
|
24
|
+
|
25
|
+
* Switch default connection pool to timed_queue on Ruby 3.4+ (jeremyevans)
|
26
|
+
|
27
|
+
* Support on_duplicate_columns={raise,warn} parameter in connection URL when using duplicate_columns_handler extension (jeremyevans)
|
28
|
+
|
29
|
+
* Add transaction_connection_validator extension for retrying transactions on new connection if there is a disconnect error when starting transaction (jeremyevans)
|
30
|
+
|
1
31
|
=== 5.76.0 (2024-01-01)
|
2
32
|
|
3
33
|
* Improve performance and flexibility of regexp matching in sqlite adapter (paddor) (#2108)
|
@@ -42,7 +42,7 @@
|
|
42
42
|
|
43
43
|
= Other Improvements
|
44
44
|
|
45
|
-
* Time/DateTime/
|
45
|
+
* Time/DateTime/SQLTime literalization speed has more than doubled
|
46
46
|
compared to the previous version. The internal code is also much
|
47
47
|
simpler, as the speedup resulted from removing multiple abstraction
|
48
48
|
layers that mostly existed for Ruby 1.8 support.
|
@@ -0,0 +1,63 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A transaction_connection_validator extension has been added. This
|
4
|
+
extension allows for transparently switching to a new connection if
|
5
|
+
a disconnect error is raised while trying to start a transaction, as
|
6
|
+
long as a connection was not already checked out from the pool
|
7
|
+
when the transaction method was called. Transparent reconnection
|
8
|
+
is safe in this case, since no user code is retried.
|
9
|
+
|
10
|
+
This extension can have lower overhead than the
|
11
|
+
connection_validator extension if that is configured to check for
|
12
|
+
validity more often than the default of one hour. However, it
|
13
|
+
only handles cases where transactions are used. It can detect
|
14
|
+
disconnects that would not be detected by default with the
|
15
|
+
connection_validator extension, since that extension defaults to
|
16
|
+
only checking validity if the connection has not been used in the
|
17
|
+
last hour.
|
18
|
+
|
19
|
+
* Sequel now supports a create_table :without_rowid option on SQLite,
|
20
|
+
to create a table WITHOUT ROWID, for better performance in some
|
21
|
+
cases. Users are encouraged to read the SQLite documentation on
|
22
|
+
WITHOUT ROWID before using this option.
|
23
|
+
|
24
|
+
* The sqlite adapter now supports a :regexp_function_cache option, if
|
25
|
+
the :setup_regexp_function option is set to :cached. The
|
26
|
+
:regexp_function_cache option should be a Proc (returning a cache
|
27
|
+
object to use), or a class. It's possible to use
|
28
|
+
ObjectSpace::WeakKeyMap as the value of the option on Ruby 3.3+
|
29
|
+
to avoid the memory leaks that are possible when using
|
30
|
+
:setup_regexp_function option :cached value with dynamic regexps.
|
31
|
+
|
32
|
+
* The duplicate_columns_handler extension now supports specifying
|
33
|
+
the on_duplicate_columns option as a connection string parameter.
|
34
|
+
|
35
|
+
= Other Improvements
|
36
|
+
|
37
|
+
* The list plugin now honors the :top option for the position when
|
38
|
+
adding the first item to the list, instead of always using 1.
|
39
|
+
|
40
|
+
* Regexp matches on SQLite are now faster on Ruby 2.4+, using
|
41
|
+
Regexp#match?.
|
42
|
+
|
43
|
+
* The uniqueness validation in the validation_helpers plugin now
|
44
|
+
uses empty? instead of count == 0, for better performance.
|
45
|
+
|
46
|
+
* On Ruby 3.4+, Sequel uses the timed_queue connection pool instead
|
47
|
+
of the threaded connection pool by default. This should make it
|
48
|
+
so no existing applications are affected by the default switch.
|
49
|
+
This should hopefully allow ample testing of the timed_queue
|
50
|
+
connection pool. At some point in the future, if no problems
|
51
|
+
are repoted, Sequel will likely switch to using the timed_queue
|
52
|
+
connection pool by default on Ruby 3.2+.
|
53
|
+
|
54
|
+
= Backwards Compatibility
|
55
|
+
|
56
|
+
* Sequel now warns by default if using eager_graph/association_join
|
57
|
+
with an association that uses a block, in the cases where the
|
58
|
+
block would be ignored and there are no appropriate graph options
|
59
|
+
set. In Sequel 6, this warning will be turned into an exception.
|
60
|
+
It is recommended that users use the auto_restrict_eager_graph
|
61
|
+
plugin to turn this into an exception now, or use the
|
62
|
+
:graph_use_association_block option so that the block is not
|
63
|
+
ignored when graphing.
|
@@ -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.
|
@@ -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
@@ -165,7 +165,7 @@ SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when run
|
|
165
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
|
166
166
|
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
167
167
|
SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
|
168
|
-
SEQUEL_CONNECTION_VALIDATOR :: Use the
|
168
|
+
SEQUEL_CONNECTION_VALIDATOR :: Use the connection_validator extension when running the adapter/integration specs
|
169
169
|
SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
|
170
170
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
171
171
|
SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
|
@@ -186,4 +186,5 @@ SEQUEL_QUERY_PER_ASSOCIATION_DB_2_URL :: Run query-per-association integration t
|
|
186
186
|
SEQUEL_QUERY_PER_ASSOCIATION_DB_3_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
187
187
|
SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
|
188
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
|
189
190
|
SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
|
@@ -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
|
@@ -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)
|
@@ -859,6 +859,41 @@ module Sequel
|
|
859
859
|
pg_class_relname(relkind, opts)
|
860
860
|
end
|
861
861
|
|
862
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which should be
|
863
|
+
# a 64-bit integer). If successful, yield to the block, then release the advisory lock
|
864
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
865
|
+
#
|
866
|
+
# DB.with_advisory_lock(1347){DB.get(1)}
|
867
|
+
# # SELECT pg_try_advisory_lock(1357) LIMIT 1
|
868
|
+
# # SELECT 1 AS v LIMIT 1
|
869
|
+
# # SELECT pg_advisory_unlock(1357) LIMIT 1
|
870
|
+
#
|
871
|
+
# Options:
|
872
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
873
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
874
|
+
ds = dataset
|
875
|
+
if server = opts[:server]
|
876
|
+
ds = ds.server(server)
|
877
|
+
end
|
878
|
+
|
879
|
+
synchronize(server) do |c|
|
880
|
+
begin
|
881
|
+
if opts[:wait]
|
882
|
+
ds.get{pg_advisory_lock(lock_id)}
|
883
|
+
locked = true
|
884
|
+
else
|
885
|
+
unless locked = ds.get{pg_try_advisory_lock(lock_id)}
|
886
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
yield
|
891
|
+
ensure
|
892
|
+
ds.get{pg_advisory_unlock(lock_id)} if locked
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
862
897
|
private
|
863
898
|
|
864
899
|
# Dataset used to retrieve CHECK constraint information
|
@@ -1785,7 +1820,7 @@ module Sequel
|
|
1785
1820
|
|
1786
1821
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1787
1822
|
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
1788
|
-
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1823
|
+
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values compounds order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1789
1824
|
Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
|
1790
1825
|
|
1791
1826
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
@@ -145,6 +145,11 @@ module Sequel
|
|
145
145
|
sqlite_version >= 30608
|
146
146
|
end
|
147
147
|
|
148
|
+
# SQLite 3.8.2+ supports the without rowid table constraint
|
149
|
+
def support_without_rowid?
|
150
|
+
sqlite_version >= 30802
|
151
|
+
end
|
152
|
+
|
148
153
|
# Override the default setting for whether to use timezones in timestamps.
|
149
154
|
# It is set to +false+ by default, as SQLite's date/time methods do not
|
150
155
|
# support timezones in timestamps.
|
@@ -344,9 +349,17 @@ module Sequel
|
|
344
349
|
ps
|
345
350
|
end
|
346
351
|
|
347
|
-
# Support creating STRICT tables via :strict
|
352
|
+
# Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
|
348
353
|
def create_table_sql(name, generator, options)
|
349
|
-
|
354
|
+
if options[:strict] && options[:without_rowid]
|
355
|
+
"#{super} STRICT, WITHOUT ROWID"
|
356
|
+
elsif options[:strict]
|
357
|
+
"#{super} STRICT"
|
358
|
+
elsif options[:without_rowid]
|
359
|
+
"#{super} WITHOUT ROWID"
|
360
|
+
else
|
361
|
+
super
|
362
|
+
end
|
350
363
|
end
|
351
364
|
|
352
365
|
# SQLite support creating temporary views.
|
@@ -118,6 +118,11 @@ module Sequel
|
|
118
118
|
# it will be called with a string for the regexp and a string
|
119
119
|
# for the value to compare, and should return whether the regexp
|
120
120
|
# matches.
|
121
|
+
# :regexp_function_cache :: If setting +setup_regexp_function+ to +cached+, this
|
122
|
+
# determines the cache to use. It should either be a proc or a class, and it
|
123
|
+
# defaults to +Hash+. You can use +ObjectSpace::WeakKeyMap+ on Ruby 3.3+ to
|
124
|
+
# have the VM automatically remove regexps from the cache after they
|
125
|
+
# are no longer used.
|
121
126
|
def connect(server)
|
122
127
|
opts = server_opts(server)
|
123
128
|
opts[:database] = ':memory:' if blank_object?(opts[:database])
|
@@ -213,10 +218,23 @@ module Sequel
|
|
213
218
|
when Proc
|
214
219
|
# nothing
|
215
220
|
when :cached, "cached"
|
216
|
-
cache =
|
217
|
-
|
221
|
+
cache = @opts[:regexp_function_cache] || Hash
|
222
|
+
cache = cache.is_a?(Proc) ? cache.call : cache.new
|
223
|
+
how = if RUBY_VERSION >= '2.4'
|
224
|
+
lambda do |regexp_str, str|
|
225
|
+
(cache[regexp_str] ||= Regexp.new(regexp_str)).match?(str)
|
226
|
+
end
|
227
|
+
else
|
228
|
+
lambda do |regexp_str, str|
|
229
|
+
(cache[regexp_str] ||= Regexp.new(regexp_str)).match(str)
|
230
|
+
end
|
231
|
+
end
|
218
232
|
else
|
219
|
-
how =
|
233
|
+
how = if RUBY_VERSION >= '2.4'
|
234
|
+
lambda{|regexp_str, str| Regexp.new(regexp_str).match?(str)}
|
235
|
+
else
|
236
|
+
lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
|
237
|
+
end
|
220
238
|
end
|
221
239
|
|
222
240
|
db.create_function("regexp", 2) do |func, regexp_str, str|
|
@@ -70,8 +70,10 @@ class Sequel::ConnectionPool
|
|
70
70
|
else
|
71
71
|
pc = if opts[:single_threaded]
|
72
72
|
opts[:servers] ? :sharded_single : :single
|
73
|
-
#
|
74
|
-
#
|
73
|
+
# :nocov:
|
74
|
+
elsif RUBY_VERSION >= '3.4' # SEQUEL6 or maybe earlier switch to 3.2
|
75
|
+
opts[:servers] ? :sharded_timed_queue : :timed_queue
|
76
|
+
# :nocov:
|
75
77
|
else
|
76
78
|
opts[:servers] ? :sharded_threaded : :threaded
|
77
79
|
end
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -149,6 +149,7 @@ module Sequel
|
|
149
149
|
@schemas = {}
|
150
150
|
@prepared_statements = {}
|
151
151
|
@transactions = {}
|
152
|
+
@transactions.compare_by_identity
|
152
153
|
@symbol_literal_cache = {}
|
153
154
|
|
154
155
|
@timezone = nil
|
@@ -263,8 +264,8 @@ module Sequel
|
|
263
264
|
# Proxy the literal call to the dataset.
|
264
265
|
#
|
265
266
|
# DB.literal(1) # 1
|
266
|
-
# DB.literal(:a) # a
|
267
|
-
# DB.literal(
|
267
|
+
# DB.literal(:a) # "a" # or `a`, [a], or a, depending on identifier quoting
|
268
|
+
# DB.literal("a") # 'a'
|
268
269
|
def literal(v)
|
269
270
|
schema_utility_dataset.literal(v)
|
270
271
|
end
|
@@ -191,6 +191,12 @@ module Sequel
|
|
191
191
|
# The +any+ type is treated like a SQLite column in a non-strict table,
|
192
192
|
# allowing any type of data to be stored. This option is supported on
|
193
193
|
# SQLite 3.37.0+.
|
194
|
+
# :without_rowid :: Create a WITHOUT ROWID table. Every row in SQLite has a special
|
195
|
+
# 'rowid' column, that uniquely identifies that row within the table.
|
196
|
+
# If this option is used, the 'rowid' column is omitted, which can
|
197
|
+
# sometimes provide some space and speed advantages. Note that you
|
198
|
+
# must then provide an explicit primary key when you create the table.
|
199
|
+
# This option is supported on SQLite 3.8.2+.
|
194
200
|
#
|
195
201
|
# See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
|
196
202
|
def create_table(name, options=OPTS, &block)
|
data/lib/sequel/exceptions.rb
CHANGED
@@ -18,6 +18,11 @@ module Sequel
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
(
|
23
|
+
# Error raised when there is a failed attempt to acquire an advisory lock.
|
24
|
+
AdvisoryLockError = Class.new(Error)
|
25
|
+
).name
|
21
26
|
|
22
27
|
(
|
23
28
|
# Error raised when the adapter requested doesn't exist or can't be loaded.
|
@@ -176,6 +176,13 @@
|
|
176
176
|
# +:preempt_async_thread+ Database option before loading the
|
177
177
|
# async_thread_pool extension.
|
178
178
|
#
|
179
|
+
# Note that the async_thread_pool extension creates the thread pool
|
180
|
+
# when it is loaded into the Database. If you fork after loading
|
181
|
+
# the extension, the extension will not work, as fork does not
|
182
|
+
# copy the thread pools. If you are using a forking webserver
|
183
|
+
# (or any other system that forks worker processes), load this
|
184
|
+
# extension in each child process, do not load it before forking.
|
185
|
+
#
|
179
186
|
# Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
|
180
187
|
|
181
188
|
|
@@ -14,12 +14,12 @@
|
|
14
14
|
#
|
15
15
|
# ds = DB[:items].extension(:duplicate_columns_handler)
|
16
16
|
#
|
17
|
-
#
|
18
|
-
# or any object that responds to :call.
|
17
|
+
# If the Database option :on_duplicate_columns is set, it configures how this
|
18
|
+
# extension works. The value should be # or any object that responds to :call.
|
19
19
|
#
|
20
|
-
# on_duplicate_columns: :raise
|
21
|
-
# on_duplicate_columns: :warn
|
22
|
-
# on_duplicate_columns: :ignore
|
20
|
+
# on_duplicate_columns: :raise # or 'raise'
|
21
|
+
# on_duplicate_columns: :warn # or 'warn'
|
22
|
+
# on_duplicate_columns: :ignore # or anything unrecognized
|
23
23
|
# on_duplicate_columns: lambda{|columns| arbitrary_condition? ? :raise : :warn}
|
24
24
|
#
|
25
25
|
# You may also configure duplicate columns handling for a specific dataset:
|
@@ -30,9 +30,10 @@
|
|
30
30
|
# ds.on_duplicate_columns{|columns| arbitrary_condition? ? :raise : :warn}
|
31
31
|
# ds.on_duplicate_columns(lambda{|columns| arbitrary_condition? ? :raise : :warn})
|
32
32
|
#
|
33
|
-
# If :raise is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
-
# If :warn is specified, you will receive a warning via +warn+.
|
33
|
+
# If :raise or 'raise' is specified, a Sequel::DuplicateColumnError is raised.
|
34
|
+
# If :warn or 'warn' is specified, you will receive a warning via +warn+.
|
35
35
|
# If a callable is specified, it will be called.
|
36
|
+
# For other values, duplicate columns are ignored (Sequel's default behavior)
|
36
37
|
# If no on_duplicate_columns is specified, the default is :warn.
|
37
38
|
#
|
38
39
|
# Related module: Sequel::DuplicateColumnsHandler
|
@@ -64,9 +65,9 @@ module Sequel
|
|
64
65
|
message = "#{caller(*CALLER_ARGS).first}: One or more duplicate columns present in #{cols.inspect}"
|
65
66
|
|
66
67
|
case duplicate_columns_handler_type(cols)
|
67
|
-
when :raise
|
68
|
+
when :raise, 'raise'
|
68
69
|
raise DuplicateColumnError, message
|
69
|
-
when :warn
|
70
|
+
when :warn, 'warn'
|
70
71
|
warn message
|
71
72
|
end
|
72
73
|
end
|
@@ -403,6 +403,11 @@ module Sequel
|
|
403
403
|
migrator_class(directory).new(db, directory, opts).is_current?
|
404
404
|
end
|
405
405
|
|
406
|
+
# Lock ID to use for advisory locks when running migrations
|
407
|
+
# "sequel-migration".codepoints.reduce(:*) % (2**63)
|
408
|
+
MIGRATION_ADVISORY_LOCK_ID = 4966325471869609408
|
409
|
+
private_constant :MIGRATION_ADVISORY_LOCK_ID
|
410
|
+
|
406
411
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
407
412
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
408
413
|
# It is very risky to use this option, since it can result in
|
@@ -416,6 +421,8 @@ module Sequel
|
|
416
421
|
# :table :: The table containing the schema version (default: :schema_info for integer migrations and
|
417
422
|
# :schema_migrations for timestamped migrations).
|
418
423
|
# :target :: The target version to which to migrate. If not given, migrates to the maximum version.
|
424
|
+
# :use_advisory_lock :: Use advisory locks in migrations (only use this if Sequel supports advisory
|
425
|
+
# locks for the database).
|
419
426
|
#
|
420
427
|
# Examples:
|
421
428
|
# Sequel::Migrator.run(DB, "migrations")
|
@@ -423,7 +430,11 @@ module Sequel
|
|
423
430
|
# Sequel::Migrator.run(DB, "app1/migrations", column: :app2_version)
|
424
431
|
# Sequel::Migrator.run(DB, "app2/migrations", column: :app2_version, table: :schema_info2)
|
425
432
|
def self.run(db, directory, opts=OPTS)
|
426
|
-
|
433
|
+
if opts[:use_advisory_lock]
|
434
|
+
db.with_advisory_lock(MIGRATION_ADVISORY_LOCK_ID){run(db, directory, opts.merge(:use_advisory_lock=>false))}
|
435
|
+
else
|
436
|
+
migrator_class(directory).new(db, directory, opts).run
|
437
|
+
end
|
427
438
|
end
|
428
439
|
|
429
440
|
# Choose the Migrator subclass to use. Uses the TimestampMigrator
|
@@ -2,27 +2,34 @@
|
|
2
2
|
#
|
3
3
|
# The sqlite_json_ops extension adds support to Sequel's DSL to make
|
4
4
|
# it easier to call SQLite JSON functions and operators (added
|
5
|
-
# first in SQLite 3.38.0).
|
5
|
+
# first in SQLite 3.38.0). It also supports the SQLite JSONB functions
|
6
|
+
# added in SQLite 3.45.0.
|
6
7
|
#
|
7
8
|
# To load the extension:
|
8
9
|
#
|
9
10
|
# Sequel.extension :sqlite_json_ops
|
10
11
|
#
|
11
|
-
# This extension works by calling methods on Sequel::SQLite::JSONOp
|
12
|
-
# which you can create
|
12
|
+
# This extension works by calling methods on Sequel::SQLite::JSONOp and
|
13
|
+
# Sequel::SQLite::JSONBOp objects, which you can create using
|
14
|
+
# Sequel.sqlite_json_op and Sequel.sqlite_jsonb_op:
|
13
15
|
#
|
14
16
|
# j = Sequel.sqlite_json_op(:json_column)
|
17
|
+
# jb = Sequel.sqlite_jsonb_op(:jsonb_column)
|
15
18
|
#
|
16
|
-
# Also, on most Sequel expression objects, you can call the sqlite_json_op
|
17
|
-
# to create a Sequel::SQLite::JSONOp
|
19
|
+
# Also, on most Sequel expression objects, you can call the sqlite_json_op or
|
20
|
+
# sqlite_jsonb_op method to create a Sequel::SQLite::JSONOp or
|
21
|
+
# Sequel::SQLite::JSONBOp object:
|
18
22
|
#
|
19
23
|
# j = Sequel[:json_column].sqlite_json_op
|
24
|
+
# jb = Sequel[:jsonb_column].sqlite_jsonb_op
|
20
25
|
#
|
21
26
|
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
22
27
|
# or you have loaded the core_refinements extension
|
23
28
|
# and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
|
29
|
+
# or Symbol#sqlite_jsonb_op:
|
24
30
|
#
|
25
31
|
# j = :json_column.sqlite_json_op
|
32
|
+
# jb = :json_column.sqlite_jsonb_op
|
26
33
|
#
|
27
34
|
# The following methods are available for Sequel::SQLite::JSONOp instances:
|
28
35
|
#
|
@@ -30,11 +37,13 @@
|
|
30
37
|
# j.get(1) # (json_column ->> 1)
|
31
38
|
# j.get_text(1) # (json_column -> 1)
|
32
39
|
# j.extract('$.a') # json_extract(json_column, '$.a')
|
40
|
+
# jb.extract('$.a') # jsonb_extract(jsonb_column, '$.a')
|
33
41
|
#
|
34
42
|
# j.array_length # json_array_length(json_column)
|
35
43
|
# j.type # json_type(json_column)
|
36
44
|
# j.valid # json_valid(json_column)
|
37
|
-
#
|
45
|
+
# jb.json # json(jsonb_column)
|
46
|
+
# j.jsonb # jsonb(json_column)
|
38
47
|
#
|
39
48
|
# j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
|
40
49
|
# j.set('$.a', 1) # json_set(json_column, '$.a', 1)
|
@@ -42,22 +51,30 @@
|
|
42
51
|
# j.remove('$.a') # json_remove(json_column, '$.a')
|
43
52
|
# j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
|
44
53
|
#
|
54
|
+
# jb.insert('$.a', 1) # jsonb_insert(jsonb_column, '$.a', 1)
|
55
|
+
# jb.set('$.a', 1) # jsonb_set(jsonb_column, '$.a', 1)
|
56
|
+
# jb.replace('$.a', 1) # jsonb_replace(jsonb_column, '$.a', 1)
|
57
|
+
# jb.remove('$.a') # jsonb_remove(jsonb_column, '$.a')
|
58
|
+
# jb.patch('{"a":2}') # jsonb_patch(jsonb_column, '{"a":2}')
|
59
|
+
#
|
45
60
|
# j.each # json_each(json_column)
|
46
61
|
# j.tree # json_tree(json_column)
|
47
62
|
#
|
48
|
-
# Related modules: Sequel::SQLite::JSONOp
|
63
|
+
# Related modules: Sequel::SQLite::JSONBaseOp, Sequel::SQLite::JSONOp,
|
64
|
+
# Sequel::SQLite::JSONBOp
|
49
65
|
|
50
66
|
#
|
51
67
|
module Sequel
|
52
68
|
module SQLite
|
53
|
-
#
|
54
|
-
# defines methods that
|
55
|
-
# SQLite json operators and functions.
|
69
|
+
# JSONBaseOp is an abstract base wrapper class for a object that
|
70
|
+
# defines methods that return Sequel expression objects representing
|
71
|
+
# SQLite json operators and functions. It is subclassed by both
|
72
|
+
# JSONOp and JSONBOp for json and jsonb specific behavior.
|
56
73
|
#
|
57
74
|
# In the method documentation examples, assume that:
|
58
75
|
#
|
59
76
|
# json_op = Sequel.sqlite_json_op(:json)
|
60
|
-
class
|
77
|
+
class JSONBaseOp < Sequel::SQL::Wrapper
|
61
78
|
GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
|
62
79
|
private_constant :GET
|
63
80
|
|
@@ -82,7 +99,7 @@ module Sequel
|
|
82
99
|
# json_op.array_length # json_array_length(json)
|
83
100
|
# json_op.array_length('$[1]') # json_array_length(json, '$[1]')
|
84
101
|
def array_length(*args)
|
85
|
-
Sequel::SQL::NumericExpression.new(:NOOP,
|
102
|
+
Sequel::SQL::NumericExpression.new(:NOOP, SQL::Function.new(:json_array_length, self, *args))
|
86
103
|
end
|
87
104
|
|
88
105
|
# Returns an expression for a set of information extracted from the top-level
|
@@ -92,7 +109,7 @@ module Sequel
|
|
92
109
|
# json_op.each # json_each(json)
|
93
110
|
# json_op.each('$.a') # json_each(json, '$.a')
|
94
111
|
def each(*args)
|
95
|
-
|
112
|
+
SQL::Function.new(:json_each, self, *args)
|
96
113
|
end
|
97
114
|
|
98
115
|
# Returns an expression for the JSON array element or object field at the specified
|
@@ -129,10 +146,17 @@ module Sequel
|
|
129
146
|
#
|
130
147
|
# json_op.json # json(json)
|
131
148
|
def json
|
132
|
-
|
149
|
+
JSONOp.new(SQL::Function.new(:json, self))
|
133
150
|
end
|
134
151
|
alias minify json
|
135
152
|
|
153
|
+
# Returns the JSONB format of the JSON.
|
154
|
+
#
|
155
|
+
# json_op.jsonb # jsonb(json)
|
156
|
+
def jsonb
|
157
|
+
JSONBOp.new(SQL::Function.new(:jsonb, self))
|
158
|
+
end
|
159
|
+
|
136
160
|
# Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
|
137
161
|
#
|
138
162
|
# json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
|
@@ -172,7 +196,7 @@ module Sequel
|
|
172
196
|
# json_op.tree # json_tree(json)
|
173
197
|
# json_op.tree('$.a') # json_tree(json, '$.a')
|
174
198
|
def tree(*args)
|
175
|
-
|
199
|
+
SQL::Function.new(:json_tree, self, *args)
|
176
200
|
end
|
177
201
|
|
178
202
|
# Returns an expression for the type of the JSON value or the JSON value at the given path.
|
@@ -180,13 +204,13 @@ module Sequel
|
|
180
204
|
# json_op.type # json_type(json)
|
181
205
|
# json_op.type('$[1]') # json_type(json, '$[1]')
|
182
206
|
def type(*args)
|
183
|
-
Sequel::SQL::StringExpression.new(:NOOP,
|
207
|
+
Sequel::SQL::StringExpression.new(:NOOP, SQL::Function.new(:json_type, self, *args))
|
184
208
|
end
|
185
209
|
alias typeof type
|
186
210
|
|
187
211
|
# Returns a boolean expression for whether the JSON is valid or not.
|
188
212
|
def valid
|
189
|
-
Sequel::SQL::BooleanExpression.new(:NOOP,
|
213
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, SQL::Function.new(:json_valid, self))
|
190
214
|
end
|
191
215
|
|
192
216
|
private
|
@@ -198,7 +222,7 @@ module Sequel
|
|
198
222
|
|
199
223
|
# Internals of the methods that return functions prefixed with +json_+.
|
200
224
|
def function(name, *args)
|
201
|
-
SQL::Function.new("
|
225
|
+
SQL::Function.new("#{function_prefix}_#{name}", self, *args)
|
202
226
|
end
|
203
227
|
|
204
228
|
# Internals of the methods that return functions prefixed with +json_+, that
|
@@ -208,12 +232,36 @@ module Sequel
|
|
208
232
|
end
|
209
233
|
end
|
210
234
|
|
235
|
+
# JSONOp is used for SQLite json-specific functions and operators.
|
236
|
+
class JSONOp < JSONBaseOp
|
237
|
+
private
|
238
|
+
|
239
|
+
def function_prefix
|
240
|
+
"json"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# JSONOp is used for SQLite jsonb-specific functions and operators.
|
245
|
+
class JSONBOp < JSONBaseOp
|
246
|
+
private
|
247
|
+
|
248
|
+
def function_prefix
|
249
|
+
"jsonb"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
211
253
|
module JSONOpMethods
|
212
254
|
# Wrap the receiver in an JSONOp so you can easily use the SQLite
|
213
255
|
# json functions and operators with it.
|
214
256
|
def sqlite_json_op
|
215
257
|
JSONOp.new(self)
|
216
258
|
end
|
259
|
+
|
260
|
+
# Wrap the receiver in an JSONBOp so you can easily use the SQLite
|
261
|
+
# jsonb functions and operators with it.
|
262
|
+
def sqlite_jsonb_op
|
263
|
+
JSONBOp.new(self)
|
264
|
+
end
|
217
265
|
end
|
218
266
|
end
|
219
267
|
|
@@ -227,6 +275,16 @@ module Sequel
|
|
227
275
|
SQLite::JSONOp.new(v)
|
228
276
|
end
|
229
277
|
end
|
278
|
+
|
279
|
+
# Return the object wrapped in an SQLite::JSONBOp.
|
280
|
+
def sqlite_jsonb_op(v)
|
281
|
+
case v
|
282
|
+
when SQLite::JSONBOp
|
283
|
+
v
|
284
|
+
else
|
285
|
+
SQLite::JSONBOp.new(v)
|
286
|
+
end
|
287
|
+
end
|
230
288
|
end
|
231
289
|
|
232
290
|
class SQL::GenericExpression
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The transaction_connection_validator extension automatically
|
4
|
+
# retries a transaction on a connection if an disconnect error
|
5
|
+
# is raised when sending the statement to begin a new
|
6
|
+
# transaction, as long as the user has not already checked out
|
7
|
+
# a connection. This is safe to do because no other queries
|
8
|
+
# have been issued on the connection, and no user-level code
|
9
|
+
# is run before retrying.
|
10
|
+
#
|
11
|
+
# This approach to connection validation can be significantly
|
12
|
+
# lower overhead than the connection_validator extension,
|
13
|
+
# though it does not handle all cases handled by the
|
14
|
+
# connection_validator extension. However, it performs the
|
15
|
+
# validation checks on every new transaction, so it will
|
16
|
+
# automatically handle disconnected connections in some cases
|
17
|
+
# where the connection_validator extension will not by default
|
18
|
+
# (as the connection_validator extension only checks
|
19
|
+
# connections if they have not been used in the last hour by
|
20
|
+
# default).
|
21
|
+
#
|
22
|
+
# Related module: Sequel::TransactionConnectionValidator
|
23
|
+
|
24
|
+
#
|
25
|
+
module Sequel
|
26
|
+
module TransactionConnectionValidator
|
27
|
+
class DisconnectRetry < DatabaseDisconnectError
|
28
|
+
# The connection that raised the disconnect error
|
29
|
+
attr_accessor :connection
|
30
|
+
|
31
|
+
# The underlying disconnect error, in case it needs to be reraised.
|
32
|
+
attr_accessor :database_error
|
33
|
+
end
|
34
|
+
|
35
|
+
# Rescue disconnect errors raised when beginning a new transaction. If there
|
36
|
+
# is a disconnnect error, it should be safe to retry the transaction using a
|
37
|
+
# new connection, as we haven't yielded control to the user yet.
|
38
|
+
def transaction(opts=OPTS)
|
39
|
+
super
|
40
|
+
rescue DisconnectRetry => e
|
41
|
+
if synchronize(opts[:server]){|conn| conn.equal?(e.connection)}
|
42
|
+
# If retrying would use the same connection, that means the
|
43
|
+
# connection was not removed from the pool, which means the caller has
|
44
|
+
# already checked out the connection, and retrying will not be successful.
|
45
|
+
# In this case, we can only reraise the exception.
|
46
|
+
raise e.database_error
|
47
|
+
end
|
48
|
+
|
49
|
+
num_retries ||= 0
|
50
|
+
num_retries += 1
|
51
|
+
retry if num_retries < 5
|
52
|
+
|
53
|
+
raise e.database_error
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Reraise disconnect errors as DisconnectRetry so they can be retried.
|
59
|
+
def begin_new_transaction(conn, opts)
|
60
|
+
super
|
61
|
+
rescue Sequel::DatabaseDisconnectError, *database_error_classes => e
|
62
|
+
if e.is_a?(Sequel::DatabaseDisconnectError) || disconnect_error?(e, OPTS)
|
63
|
+
exception = DisconnectRetry.new(e.message)
|
64
|
+
exception.set_backtrace([])
|
65
|
+
exception.connection = conn
|
66
|
+
unless e.is_a?(Sequel::DatabaseError)
|
67
|
+
e = Sequel.convert_exception_class(e, database_error_class(e, OPTS))
|
68
|
+
end
|
69
|
+
exception.database_error = e
|
70
|
+
raise exception
|
71
|
+
end
|
72
|
+
|
73
|
+
raise
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Database.register_extension(:transaction_connection_validator, TransactionConnectionValidator)
|
78
|
+
end
|
@@ -3387,8 +3387,15 @@ module Sequel
|
|
3387
3387
|
local_opts = ds.opts[:eager_graph][:local]
|
3388
3388
|
limit_strategy = r.eager_graph_limit_strategy(local_opts[:limit_strategy])
|
3389
3389
|
|
3390
|
-
|
3391
|
-
|
3390
|
+
# SEQUEL6: remove and integrate the auto_restrict_eager_graph plugin
|
3391
|
+
if !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block) && !r[:allow_eager_graph]
|
3392
|
+
if r[:conditions] && !Sequel.condition_specifier?(r[:conditions])
|
3393
|
+
raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
|
3394
|
+
end
|
3395
|
+
|
3396
|
+
if r[:block] && !r[:graph_use_association_block]
|
3397
|
+
warn "eager_graph used for association when association given a block without graph options. The block is ignored in this case. This will result in an exception starting in Sequel 6. Model: #{r[:model]}, association: #{r[:name]}"
|
3398
|
+
end
|
3392
3399
|
end
|
3393
3400
|
|
3394
3401
|
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1244,18 +1244,21 @@ module Sequel
|
|
1244
1244
|
@errors ||= errors_class.new
|
1245
1245
|
end
|
1246
1246
|
|
1247
|
+
EXISTS_SELECT_ = SQL::AliasedExpression.new(1, :one)
|
1248
|
+
private_constant :EXISTS_SELECT_
|
1249
|
+
|
1247
1250
|
# Returns true when current instance exists, false otherwise.
|
1248
1251
|
# Generally an object that isn't new will exist unless it has
|
1249
1252
|
# been deleted. Uses a database query to check for existence,
|
1250
1253
|
# unless the model object is new, in which case this is always
|
1251
1254
|
# false.
|
1252
1255
|
#
|
1253
|
-
# Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
|
1256
|
+
# Artist[1].exists? # SELECT 1 AS one FROM artists WHERE (id = 1)
|
1254
1257
|
# # => true
|
1255
1258
|
# Artist.new.exists?
|
1256
1259
|
# # => false
|
1257
1260
|
def exists?
|
1258
|
-
new? ? false : !this.get(
|
1261
|
+
new? ? false : !this.get(EXISTS_SELECT_).nil?
|
1259
1262
|
end
|
1260
1263
|
|
1261
1264
|
# Ignore the model's setter method cache when this instances extends a module, as the
|
data/lib/sequel/plugins/list.rb
CHANGED
@@ -185,10 +185,13 @@ module Sequel
|
|
185
185
|
end
|
186
186
|
|
187
187
|
# Set the value of the position_field to the maximum value plus 1 unless the
|
188
|
-
# position field already has a value.
|
188
|
+
# position field already has a value. If the list is empty, the position will
|
189
|
+
# be set to the model's +top_of_list+ value.
|
189
190
|
def before_validation
|
190
191
|
unless get_column_value(position_field)
|
191
|
-
|
192
|
+
current_max = list_dataset.max(position_field)
|
193
|
+
value = current_max.nil? ? model.top_of_list : current_max.to_i + 1
|
194
|
+
set_column_value("#{position_field}=", value)
|
192
195
|
end
|
193
196
|
super
|
194
197
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 78
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.78.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -223,6 +223,8 @@ extra_rdoc_files:
|
|
223
223
|
- doc/release_notes/5.74.0.txt
|
224
224
|
- doc/release_notes/5.75.0.txt
|
225
225
|
- doc/release_notes/5.76.0.txt
|
226
|
+
- doc/release_notes/5.77.0.txt
|
227
|
+
- doc/release_notes/5.78.0.txt
|
226
228
|
- doc/release_notes/5.8.0.txt
|
227
229
|
- doc/release_notes/5.9.0.txt
|
228
230
|
files:
|
@@ -327,6 +329,8 @@ files:
|
|
327
329
|
- doc/release_notes/5.74.0.txt
|
328
330
|
- doc/release_notes/5.75.0.txt
|
329
331
|
- doc/release_notes/5.76.0.txt
|
332
|
+
- doc/release_notes/5.77.0.txt
|
333
|
+
- doc/release_notes/5.78.0.txt
|
330
334
|
- doc/release_notes/5.8.0.txt
|
331
335
|
- doc/release_notes/5.9.0.txt
|
332
336
|
- doc/schema_modification.rdoc
|
@@ -515,6 +519,7 @@ files:
|
|
515
519
|
- lib/sequel/extensions/synchronize_sql.rb
|
516
520
|
- lib/sequel/extensions/thread_local_timezones.rb
|
517
521
|
- lib/sequel/extensions/to_dot.rb
|
522
|
+
- lib/sequel/extensions/transaction_connection_validator.rb
|
518
523
|
- lib/sequel/extensions/virtual_row_method_block.rb
|
519
524
|
- lib/sequel/model.rb
|
520
525
|
- lib/sequel/model/associations.rb
|