sequel 5.80.0 → 5.82.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/bin/sequel +17 -4
- data/doc/code_order.rdoc +5 -3
- data/doc/opening_databases.rdoc +3 -0
- data/doc/release_notes/5.81.0.txt +31 -0
- data/doc/release_notes/5.82.0.txt +61 -0
- data/doc/testing.rdoc +14 -0
- data/lib/sequel/adapters/shared/postgres.rb +24 -3
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
- data/lib/sequel/connection_pool/threaded.rb +26 -10
- data/lib/sequel/database/connecting.rb +19 -22
- data/lib/sequel/database/misc.rb +3 -0
- data/lib/sequel/extensions/caller_logging.rb +2 -0
- data/lib/sequel/extensions/provenance.rb +2 -0
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/plugins/association_pks.rb +1 -1
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
- data/lib/sequel/plugins/validate_associated.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: f09a139a164322e0a0e52e9a58c79b2f463972363509c41bf50e7082c1f542b7
|
4
|
+
data.tar.gz: e7222b81a79111b9853565160f81cb18c72b341f18bec839046c9b49a4b199ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9f16395ad855ccbc4494bd2e1436b6b78f15ddbfd1c9b80d356040d99415a46a352f50042b2fc9ab23d29b65567dafec2fceb35c709498159b00a1402991ee6
|
7
|
+
data.tar.gz: a5693c2f3c3791c0216a5af9e933edaa41b0eeb030a3523e52a6951c666e720d0b12ec48ae3f46777ce617b27857f6519d7ce98ec1768a0c8226c4b494e501c1
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 5.82.0 (2024-07-01)
|
2
|
+
|
3
|
+
* Limit tactically eager loading to objects that have the same association reflection (jeremyevans) (#2181)
|
4
|
+
|
5
|
+
* Fix race condition in threaded/sharded_threaded connection pools that could cause stalls (jeremyevans)
|
6
|
+
|
7
|
+
* Emulate dropping a unique column or a column that is part of an index on SQLite 3.35.0+ (jeremyevans) (#2176)
|
8
|
+
|
9
|
+
* Support MERGE RETURNING on PostgreSQL 17+ (jeremyevans)
|
10
|
+
|
11
|
+
* Remove use of logger library in bin/sequel (jeremyevans)
|
12
|
+
|
13
|
+
* Support :connect_opts_proc Database option for late binding options (jeremyevans) (#2164)
|
14
|
+
|
15
|
+
=== 5.81.0 (2024-06-01)
|
16
|
+
|
17
|
+
* Fix ignored block warnings in a couple plugin apply methods on Ruby 3.4 (jeremyevans)
|
18
|
+
|
19
|
+
* Skip Ruby internal caller locations when searching for caller locations in caller_logging and provenance extensions (jeremyevans)
|
20
|
+
|
21
|
+
* Add temporarily_release_connection Database extension for multithreaded transactional testing (jeremyevans)
|
22
|
+
|
1
23
|
=== 5.80.0 (2024-05-01)
|
2
24
|
|
3
25
|
* Support Dataset#skip_locked on MariaDB 10.6+ (simi) (#2150)
|
data/bin/sequel
CHANGED
@@ -18,6 +18,21 @@ load_dirs = []
|
|
18
18
|
exclusive_options = []
|
19
19
|
loggers = []
|
20
20
|
|
21
|
+
logger_class = Class.new do
|
22
|
+
def initialize(device)
|
23
|
+
@device = device
|
24
|
+
end
|
25
|
+
|
26
|
+
def debug(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
[:info, :warn, :error].each do |meth|
|
30
|
+
define_method(meth) do |msg|
|
31
|
+
@device.puts("#{Time.now.strftime('%Y-%m-%d %T')} #{meth.to_s.upcase}: #{msg}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
21
36
|
options = OptionParser.new do |opts|
|
22
37
|
opts.banner = "Sequel: The Database Toolkit for Ruby"
|
23
38
|
opts.define_head "Usage: sequel [options] <uri|path> [file]"
|
@@ -61,8 +76,7 @@ options = OptionParser.new do |opts|
|
|
61
76
|
end
|
62
77
|
|
63
78
|
opts.on("-E", "--echo", "echo SQL statements") do
|
64
|
-
|
65
|
-
loggers << Logger.new($stdout)
|
79
|
+
loggers << logger_class.new($stdout)
|
66
80
|
end
|
67
81
|
|
68
82
|
opts.on("-I", "--include dir", "specify $LOAD_PATH directory") do |v|
|
@@ -70,8 +84,7 @@ options = OptionParser.new do |opts|
|
|
70
84
|
end
|
71
85
|
|
72
86
|
opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
|
73
|
-
|
74
|
-
loggers << Logger.new(v)
|
87
|
+
loggers << logger_class.new(File.open(v, 'a'))
|
75
88
|
end
|
76
89
|
|
77
90
|
opts.on("-L", "--load-dir DIR", "loads all *.rb under specifed directory") do |v|
|
data/doc/code_order.rdoc
CHANGED
@@ -91,9 +91,11 @@ unsafe runtime modification of the configuration:
|
|
91
91
|
model_classes.each(&:freeze)
|
92
92
|
DB.freeze
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
94
|
+
`model_classes` is not a Sequel method, it indicates an array of model
|
95
|
+
classes you defined. Instead of listing them manually, the `subclasses`
|
96
|
+
plugin can be used to keep track of all model classes that have been
|
97
|
+
setup in your application. Finalizing their associations and freezing
|
98
|
+
them can easily be achieved through the plugin:
|
97
99
|
|
98
100
|
# Register the plugin before setting up the models
|
99
101
|
Sequel::Model.plugin :subclasses
|
data/doc/opening_databases.rdoc
CHANGED
@@ -79,6 +79,9 @@ These options are shared by all adapters unless otherwise noted.
|
|
79
79
|
or string with extensions separated by columns. These extensions are loaded after
|
80
80
|
connections are made by the :preconnect option.
|
81
81
|
:cache_schema :: Whether schema should be cached for this database (true by default)
|
82
|
+
:connect_opts_proc :: Callable object for modifying options hash used when connecting, designed for
|
83
|
+
cases where the option values (e.g. password) are automatically rotated on
|
84
|
+
a regular basis without involvement from the application using Sequel.
|
82
85
|
:default_string_column_size :: The default size for string columns (255 by default)
|
83
86
|
:host :: The hostname of the database server to which to connect
|
84
87
|
:keep_reference :: Whether to keep a reference to the database in Sequel::DATABASES (true by default)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A temporarily_release_connection Database extension has been added,
|
4
|
+
designed for multithreaded transactional testing.
|
5
|
+
|
6
|
+
This allows one thread to start a transaction, and then release
|
7
|
+
the connection back for usage by the connection pool, so that
|
8
|
+
other threads can operate on the connection object safely inside
|
9
|
+
the transaction. This requires the connection pool be limited
|
10
|
+
to a single connection, to ensure that the released connection
|
11
|
+
can be reacquired. It's not perfect, because if the connection
|
12
|
+
is disconnected and removed from the pool while temporarily
|
13
|
+
released, there is no way to handle that situation correctly.
|
14
|
+
Example:
|
15
|
+
|
16
|
+
DB.transaction(rollback: :always, auto_savepoint: true) do |conn|
|
17
|
+
DB.temporarily_release_connection(conn) do
|
18
|
+
# Other threads can operate on connection safely inside
|
19
|
+
# the transaction
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
= Other Improvements
|
25
|
+
|
26
|
+
* In the caller_logging and provenance extensions, Ruby internal
|
27
|
+
caller locations are skipped when trying to locate the appropriate
|
28
|
+
caller line to include.
|
29
|
+
|
30
|
+
* A couple ignored block warnings in plugin apply methods have been
|
31
|
+
fixed on Ruby 3.4.
|
@@ -0,0 +1,61 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* MERGE RETURNING is now supported when using PostgreSQL 17+. For
|
4
|
+
datasets supporting RETURNING, calling merge with a block
|
5
|
+
will yield each returned row:
|
6
|
+
|
7
|
+
DB[:table1].
|
8
|
+
returning.
|
9
|
+
merge_using(:table2, column1: :column2).
|
10
|
+
merge_insert(column3: :column4).
|
11
|
+
merge do |row|
|
12
|
+
# ...
|
13
|
+
end
|
14
|
+
# MERGE INTO "table1" USING "table2"
|
15
|
+
# ON ("column1" = "column2")
|
16
|
+
# WHEN NOT MATCHED THEN
|
17
|
+
# INSERT ("column3") VALUES ("column3")
|
18
|
+
# RETURNING *
|
19
|
+
|
20
|
+
* A :connect_opts_proc Database option is now supported, to allow
|
21
|
+
support for late-binding Database options. If provided, this
|
22
|
+
should be a callable object that is called with the options
|
23
|
+
used for connecting, and can modify the options. This makes
|
24
|
+
it simple to support authentication schemes that rotate
|
25
|
+
passwords automatically without user involvement:
|
26
|
+
|
27
|
+
Sequel.connect('postgres://user@host/database',
|
28
|
+
connect_opts_proc: lambda do |opts|
|
29
|
+
opts[:password] = SomeAuthLibrary.get_current_password(opts[:user])
|
30
|
+
end)
|
31
|
+
|
32
|
+
Note that the jdbc adapter relies on URIs and not option hashes,
|
33
|
+
so when using the jdbc adapter with this feature, you'll generally
|
34
|
+
need to set the :uri option.
|
35
|
+
|
36
|
+
= Other Improvements
|
37
|
+
|
38
|
+
* A race condition in the threaded connection pools that could result
|
39
|
+
in a delay or timeout error in checking out connections in low-traffic
|
40
|
+
environments has been fixed.
|
41
|
+
|
42
|
+
* Sequel now supports dropping a unique column or a column that is
|
43
|
+
part of an index on SQLite 3.35.0+, with the same emulation approach
|
44
|
+
it uses in earlier SQLite versions.
|
45
|
+
|
46
|
+
* The tactical_eager_loading plugin now handles cases where inheritance
|
47
|
+
is used and the objects used include associations with the same name
|
48
|
+
but different definitions. Sequel will now only eager load the
|
49
|
+
association for objects that use the same association definition as
|
50
|
+
the receiver.
|
51
|
+
|
52
|
+
= Backwards Compatibility
|
53
|
+
|
54
|
+
* bin/sequel no longer requires logger if passed the -E or -l options.
|
55
|
+
Instead, it uses a simple implementation that supports only
|
56
|
+
debug/warn/info/error methods. If you are using bin/sequel and
|
57
|
+
depending on the log format produced by the logger library, or
|
58
|
+
calling methods on the logger object other than
|
59
|
+
debug/warn/info/error, you'll need to update your code. This change
|
60
|
+
was made because logger is moving out of stdlib in a future Ruby
|
61
|
+
version.
|
data/doc/testing.rdoc
CHANGED
@@ -47,6 +47,20 @@ These run each test in its own transaction, the recommended way to test.
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
== Transactional testing with multiple threads
|
51
|
+
|
52
|
+
Some tests may require executing code across multiple threads. The most common example are browser tests with Capybara, where the web server is running in a separate thread. For transactional tests to work in this case, the main thread needs to allow other threads to use its database connection while the transaction is in progress. This can be achieved with the temporarily_release_connection extension:
|
53
|
+
|
54
|
+
DB.extension :temporarily_release_connection
|
55
|
+
DB.transaction(rollback: :always, auto_savepoint: true) do |conn|
|
56
|
+
DB.temporarily_release_connection(conn) do
|
57
|
+
# Other threads can operate on connection safely inside the transaction
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
This requires maximum connection pool size to be 1, so make sure to set the Database +:max_connections+ option to 1 in tests.
|
63
|
+
|
50
64
|
== Transactional testing with multiple databases
|
51
65
|
|
52
66
|
You can use the Sequel.transaction method to run a transaction on multiple databases, rolling all of them back. Instead of:
|
@@ -2058,6 +2058,16 @@ module Sequel
|
|
2058
2058
|
nil
|
2059
2059
|
end
|
2060
2060
|
|
2061
|
+
# Support MERGE RETURNING on PostgreSQL 17+.
|
2062
|
+
def merge(&block)
|
2063
|
+
sql = merge_sql
|
2064
|
+
if uses_returning?(:merge)
|
2065
|
+
returning_fetch_rows(sql, &block)
|
2066
|
+
else
|
2067
|
+
execute_ddl(sql)
|
2068
|
+
end
|
2069
|
+
end
|
2070
|
+
|
2061
2071
|
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
2062
2072
|
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2063
2073
|
# use it as additional conditions for the match.
|
@@ -2087,7 +2097,7 @@ module Sequel
|
|
2087
2097
|
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
2088
2098
|
def merge_insert(*values, &block)
|
2089
2099
|
h = {:type=>:insert, :values=>values}
|
2090
|
-
if
|
2100
|
+
if @opts[:override]
|
2091
2101
|
h[:override] = insert_override_sql(String.new)
|
2092
2102
|
end
|
2093
2103
|
_merge_when(h, &block)
|
@@ -2170,9 +2180,14 @@ module Sequel
|
|
2170
2180
|
true
|
2171
2181
|
end
|
2172
2182
|
|
2173
|
-
#
|
2183
|
+
# MERGE RETURNING is supported on PostgreSQL 17+. Other RETURNING is supported
|
2184
|
+
# on all supported PostgreSQL versions.
|
2174
2185
|
def supports_returning?(type)
|
2175
|
-
|
2186
|
+
if type == :merge
|
2187
|
+
server_version >= 170000
|
2188
|
+
else
|
2189
|
+
true
|
2190
|
+
end
|
2176
2191
|
end
|
2177
2192
|
|
2178
2193
|
# PostgreSQL supports pattern matching via regular expressions
|
@@ -2295,6 +2310,12 @@ module Sequel
|
|
2295
2310
|
end
|
2296
2311
|
alias _merge_not_matched_sql _merge_matched_sql
|
2297
2312
|
|
2313
|
+
# Support MERGE RETURNING on PostgreSQL 17+.
|
2314
|
+
def _merge_when_sql(sql)
|
2315
|
+
super
|
2316
|
+
insert_returning_sql(sql) if uses_returning?(:merge)
|
2317
|
+
end
|
2318
|
+
|
2298
2319
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
2299
2320
|
def _truncate_sql(table)
|
2300
2321
|
to = @opts[:truncate_opts] || OPTS
|
@@ -245,7 +245,7 @@ module Sequel
|
|
245
245
|
super
|
246
246
|
end
|
247
247
|
when :drop_column
|
248
|
-
if sqlite_version >= 33500
|
248
|
+
if sqlite_version >= 33500 && !indexes(table).any?{|_, h| h[:columns].include?(op[:name])}
|
249
249
|
super
|
250
250
|
else
|
251
251
|
ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
|
@@ -197,11 +197,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
197
197
|
timeout = @timeout
|
198
198
|
timer = Sequel.start_timer
|
199
199
|
|
200
|
-
|
201
|
-
|
202
|
-
if conn = next_available(server)
|
203
|
-
return(allocated(server)[thread] = conn)
|
204
|
-
end
|
200
|
+
if conn = acquire_available(thread, server, timeout)
|
201
|
+
return conn
|
205
202
|
end
|
206
203
|
|
207
204
|
until conn = assign_connection(thread, server)
|
@@ -211,11 +208,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
211
208
|
|
212
209
|
# It's difficult to get to this point, it can only happen if there is a race condition
|
213
210
|
# where a connection cannot be acquired even after the thread is signalled by the condition variable
|
214
|
-
|
215
|
-
|
216
|
-
if conn = next_available(server)
|
217
|
-
return(allocated(server)[thread] = conn)
|
218
|
-
end
|
211
|
+
if conn = acquire_available(thread, server, timeout - elapsed)
|
212
|
+
return conn
|
219
213
|
end
|
220
214
|
# :nocov:
|
221
215
|
end
|
@@ -223,6 +217,28 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
223
217
|
conn
|
224
218
|
end
|
225
219
|
|
220
|
+
# Acquire a connection if one is already available, or waiting until it becomes available.
|
221
|
+
def acquire_available(thread, server, timeout)
|
222
|
+
sync do
|
223
|
+
# Check if connection was checked in between when assign_connection failed and now.
|
224
|
+
# This is very unlikely, but necessary to prevent a situation where the waiter
|
225
|
+
# will wait for a connection even though one has already been checked in.
|
226
|
+
# :nocov:
|
227
|
+
if conn = next_available(server)
|
228
|
+
return(allocated(server)[thread] = conn)
|
229
|
+
end
|
230
|
+
# :nocov:
|
231
|
+
|
232
|
+
@waiters[server].wait(@mutex, timeout)
|
233
|
+
|
234
|
+
# Connection still not available, could be because a connection was disconnected,
|
235
|
+
# may have to retry assign_connection to see if a new connection can be made.
|
236
|
+
if conn = next_available(server)
|
237
|
+
return(allocated(server)[thread] = conn)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
226
242
|
# Assign a connection to the thread, or return nil if one cannot be assigned.
|
227
243
|
# The caller should NOT have the mutex before calling this.
|
228
244
|
def assign_connection(thread, server)
|
@@ -143,11 +143,8 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
143
143
|
timeout = @timeout
|
144
144
|
timer = Sequel.start_timer
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
if conn = next_available
|
149
|
-
return(@allocated[thread] = conn)
|
150
|
-
end
|
146
|
+
if conn = acquire_available(thread, timeout)
|
147
|
+
return conn
|
151
148
|
end
|
152
149
|
|
153
150
|
until conn = assign_connection(thread)
|
@@ -157,11 +154,8 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
157
154
|
|
158
155
|
# It's difficult to get to this point, it can only happen if there is a race condition
|
159
156
|
# where a connection cannot be acquired even after the thread is signalled by the condition variable
|
160
|
-
|
161
|
-
|
162
|
-
if conn = next_available
|
163
|
-
return(@allocated[thread] = conn)
|
164
|
-
end
|
157
|
+
if conn = acquire_available(thread, timeout - elapsed)
|
158
|
+
return conn
|
165
159
|
end
|
166
160
|
# :nocov:
|
167
161
|
end
|
@@ -169,6 +163,28 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
169
163
|
conn
|
170
164
|
end
|
171
165
|
|
166
|
+
# Acquire a connection if one is already available, or waiting until it becomes available.
|
167
|
+
def acquire_available(thread, timeout)
|
168
|
+
sync do
|
169
|
+
# Check if connection was checked in between when assign_connection failed and now.
|
170
|
+
# This is very unlikely, but necessary to prevent a situation where the waiter
|
171
|
+
# will wait for a connection even though one has already been checked in.
|
172
|
+
# :nocov:
|
173
|
+
if conn = next_available
|
174
|
+
return(@allocated[thread] = conn)
|
175
|
+
end
|
176
|
+
# :nocov:
|
177
|
+
|
178
|
+
@waiter.wait(@mutex, timeout)
|
179
|
+
|
180
|
+
# Connection still not available, could be because a connection was disconnected,
|
181
|
+
# may have to retry assign_connection to see if a new connection can be made.
|
182
|
+
if conn = next_available
|
183
|
+
return(@allocated[thread] = conn)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
172
188
|
# Assign a connection to the thread, or return nil if one cannot be assigned.
|
173
189
|
# The caller should NOT have the mutex before calling this.
|
174
190
|
def assign_connection(thread)
|
@@ -270,28 +270,20 @@ module Sequel
|
|
270
270
|
@single_threaded
|
271
271
|
end
|
272
272
|
|
273
|
-
|
274
|
-
#
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
#
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
# server, instead of the :default server.
|
288
|
-
#
|
289
|
-
# DB.synchronize do |conn|
|
290
|
-
# # ...
|
291
|
-
# end
|
292
|
-
def synchronize(server=nil, &block)
|
293
|
-
@pool.hold(server || :default, &block)
|
294
|
-
end
|
273
|
+
# Acquires a database connection, yielding it to the passed block. This is
|
274
|
+
# useful if you want to make sure the same connection is used for all
|
275
|
+
# database queries in the block. It is also useful if you want to gain
|
276
|
+
# direct access to the underlying connection object if you need to do
|
277
|
+
# something Sequel does not natively support.
|
278
|
+
#
|
279
|
+
# If a server option is given, acquires a connection for that specific
|
280
|
+
# server, instead of the :default server.
|
281
|
+
#
|
282
|
+
# DB.synchronize do |conn|
|
283
|
+
# # ...
|
284
|
+
# end
|
285
|
+
def synchronize(server=nil, &block)
|
286
|
+
@pool.hold(server || :default, &block)
|
295
287
|
end
|
296
288
|
|
297
289
|
# Attempts to acquire a database connection. Returns true if successful.
|
@@ -343,6 +335,11 @@ module Sequel
|
|
343
335
|
else
|
344
336
|
@opts.dup
|
345
337
|
end
|
338
|
+
|
339
|
+
if pr = opts[:connect_opts_proc]
|
340
|
+
pr.call(opts)
|
341
|
+
end
|
342
|
+
|
346
343
|
opts.delete(:servers)
|
347
344
|
opts
|
348
345
|
end
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -108,6 +108,9 @@ module Sequel
|
|
108
108
|
# :cache_schema :: Whether schema should be cached for this Database instance
|
109
109
|
# :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
|
110
110
|
# :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
111
|
+
# :connect_opts_proc :: Callable object for modifying options hash used when connecting, designed for
|
112
|
+
# cases where the option values (e.g. password) are automatically rotated on
|
113
|
+
# a regular basis without involvement from the application using Sequel.
|
111
114
|
# :default_string_column_size :: The default size of string columns, 255 by default.
|
112
115
|
# :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
|
113
116
|
# or string with extensions separated by columns. These extensions are loaded after
|
@@ -37,6 +37,7 @@ module Sequel
|
|
37
37
|
module CallerLogging
|
38
38
|
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
39
39
|
RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
|
40
|
+
INTERNAL = '<internal'
|
40
41
|
|
41
42
|
# A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
|
42
43
|
attr_accessor :caller_logging_ignore
|
@@ -61,6 +62,7 @@ module Sequel
|
|
61
62
|
c = caller.find do |line|
|
62
63
|
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
63
64
|
line.start_with?(RUBY_STDLIB) ||
|
65
|
+
line.start_with?(INTERNAL) ||
|
64
66
|
(ignore && line =~ ignore))
|
65
67
|
end
|
66
68
|
|
@@ -38,6 +38,7 @@ module Sequel
|
|
38
38
|
module Provenance
|
39
39
|
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
40
40
|
RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
|
41
|
+
INTERNAL = '<internal'
|
41
42
|
|
42
43
|
if TRUE_FREEZE
|
43
44
|
# Include provenance information when cloning datasets.
|
@@ -98,6 +99,7 @@ module Sequel
|
|
98
99
|
caller.find do |line|
|
99
100
|
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
100
101
|
line.start_with?(RUBY_STDLIB) ||
|
102
|
+
line.start_with?(INTERNAL) ||
|
101
103
|
(ignore && line =~ ignore))
|
102
104
|
end
|
103
105
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The temporarily_release_connection extension adds support for temporarily
|
4
|
+
# releasing a checked out connection back to the connection pool. It is
|
5
|
+
# designed for use in multithreaded transactional integration tests, allowing
|
6
|
+
# a connection to start a transaction in one thread, but be temporarily
|
7
|
+
# released back to the connection pool, so it can be operated on safely
|
8
|
+
# by multiple threads inside a block. For example, the main thread could be
|
9
|
+
# running tests that send web requests, and a separate thread running a web
|
10
|
+
# server that is responding to those requests, and the same connection and
|
11
|
+
# transaction would be used for both.
|
12
|
+
#
|
13
|
+
# To load the extension into the database:
|
14
|
+
#
|
15
|
+
# DB.extension :temporarily_release_connection
|
16
|
+
#
|
17
|
+
# After the extension is loaded, call the +temporarily_release_connection+
|
18
|
+
# method with the connection object to temporarily release the connection
|
19
|
+
# back to the pool. Example:
|
20
|
+
#
|
21
|
+
# DB.transaction(rollback: :always, auto_savepoint: true) do |conn|
|
22
|
+
# DB.temporarily_release_connection(conn) do
|
23
|
+
# # Other threads can operate on connection safely inside the transaction
|
24
|
+
# yield
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# For sharded connection pools, the second argument to +temporarily_release_connection+
|
29
|
+
# is respected, and specifies the server on which to temporarily release the connection.
|
30
|
+
#
|
31
|
+
# The temporarily_release_connection extension is only supported with the
|
32
|
+
# threaded and timed_queue connection pools that ship with Sequel (and the sharded
|
33
|
+
# versions of each). To make sure that same connection object can be reacquired, it
|
34
|
+
# is only supported if the maximum connection pool size is 1, so set the Database
|
35
|
+
# :max_connections option to 1 if you plan to use this extension.
|
36
|
+
#
|
37
|
+
# If the +temporarily_release_connection+ method cannot reacquire the same connection
|
38
|
+
# it released to the pool, it will raise a Sequel::UnableToReacquireConnectionError
|
39
|
+
# exception. This should only happen if the connection has been disconnected
|
40
|
+
# while it was temporarily released. If this error is raised, Database#transaction
|
41
|
+
# will not rollback the transaction, since the connection object is likely no longer
|
42
|
+
# valid, and on poorly written database drivers, that could cause the process to crash.
|
43
|
+
#
|
44
|
+
# Related modules: Sequel::TemporarilyReleaseConnection,
|
45
|
+
# Sequel::UnableToReacquireConnectionError
|
46
|
+
|
47
|
+
#
|
48
|
+
module Sequel
|
49
|
+
# Error class raised if the connection pool does not provide the same connection
|
50
|
+
# object when checking a temporarily released connection out.
|
51
|
+
class UnableToReacquireConnectionError < Error
|
52
|
+
end
|
53
|
+
|
54
|
+
module TemporarilyReleaseConnection
|
55
|
+
module DatabaseMethods
|
56
|
+
# Temporarily release the connection back to the connection pool for the
|
57
|
+
# duration of the block.
|
58
|
+
def temporarily_release_connection(conn, server=:default, &block)
|
59
|
+
pool.temporarily_release_connection(conn, server, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Do nothing if UnableToReacquireConnectionError is raised, as it is
|
65
|
+
# likely the connection is not in a usable state.
|
66
|
+
def rollback_transaction(conn, opts)
|
67
|
+
return if UnableToReacquireConnectionError === $!
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module PoolMethods
|
73
|
+
# Temporarily release a currently checked out connection, then yield to the block. Reacquire the same
|
74
|
+
# connection upon the exit of the block.
|
75
|
+
def temporarily_release_connection(conn, server)
|
76
|
+
t = Sequel.current
|
77
|
+
raise Error, "connection not currently checked out" unless conn.equal?(trc_owned_connection(t, server))
|
78
|
+
|
79
|
+
begin
|
80
|
+
trc_release(t, conn, server)
|
81
|
+
yield
|
82
|
+
ensure
|
83
|
+
c = trc_acquire(t, server)
|
84
|
+
unless conn.equal?(c)
|
85
|
+
raise UnableToReacquireConnectionError, "reacquired connection not the same as initial connection"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
module TimedQueue
|
92
|
+
private
|
93
|
+
|
94
|
+
def trc_owned_connection(t, server)
|
95
|
+
owned_connection(t)
|
96
|
+
end
|
97
|
+
|
98
|
+
def trc_release(t, conn, server)
|
99
|
+
release(t)
|
100
|
+
end
|
101
|
+
|
102
|
+
def trc_acquire(t, server)
|
103
|
+
acquire(t)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module ShardedTimedQueue
|
108
|
+
# Normalize the server name for sharded connection pools
|
109
|
+
def temporarily_release_connection(conn, server)
|
110
|
+
server = pick_server(server)
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def trc_owned_connection(t, server)
|
117
|
+
owned_connection(t, server)
|
118
|
+
end
|
119
|
+
|
120
|
+
def trc_release(t, conn, server)
|
121
|
+
release(t, conn, server)
|
122
|
+
end
|
123
|
+
|
124
|
+
def trc_acquire(t, server)
|
125
|
+
acquire(t, server)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module ThreadedBase
|
130
|
+
private
|
131
|
+
|
132
|
+
def trc_release(t, conn, server)
|
133
|
+
sync{super}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Threaded
|
138
|
+
include TimedQueue
|
139
|
+
include ThreadedBase
|
140
|
+
end
|
141
|
+
|
142
|
+
module ShardedThreaded
|
143
|
+
include ShardedTimedQueue
|
144
|
+
include ThreadedBase
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
trc = TemporarilyReleaseConnection
|
149
|
+
trc_map = {
|
150
|
+
:threaded => trc::Threaded,
|
151
|
+
:sharded_threaded => trc::ShardedThreaded,
|
152
|
+
:timed_queue => trc::TimedQueue,
|
153
|
+
:sharded_timed_queue => trc::ShardedTimedQueue,
|
154
|
+
}.freeze
|
155
|
+
|
156
|
+
Database.register_extension(:temporarily_release_connection) do |db|
|
157
|
+
unless pool_mod = trc_map[db.pool.pool_type]
|
158
|
+
raise(Error, "temporarily_release_connection extension not supported for connection pool type #{db.pool.pool_type}")
|
159
|
+
end
|
160
|
+
|
161
|
+
case db.pool.pool_type
|
162
|
+
when :threaded, :sharded_threaded
|
163
|
+
if db.opts[:connection_handling] == :disconnect
|
164
|
+
raise Error, "temporarily_release_connection extension not supported with connection_handling: :disconnect option"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
unless db.pool.max_size == 1
|
169
|
+
raise Error, "temporarily_release_connection extension not supported unless :max_connections option is 1"
|
170
|
+
end
|
171
|
+
|
172
|
+
db.extend(trc::DatabaseMethods)
|
173
|
+
db.pool.extend(trc::PoolMethods)
|
174
|
+
db.pool.extend(pool_mod)
|
175
|
+
end
|
176
|
+
|
177
|
+
private_constant :TemporarilyReleaseConnection
|
178
|
+
end
|
@@ -294,7 +294,7 @@ module Sequel
|
|
294
294
|
sch = klass.db_schema
|
295
295
|
|
296
296
|
if primary_key.is_a?(Array)
|
297
|
-
if (cols = sch.values_at(*klass.primary_key)).all? && (
|
297
|
+
if (cols = sch.values_at(*klass.primary_key)).all? && (cols.map{|c| c[:type] == :integer}).all?
|
298
298
|
db = model.db
|
299
299
|
pks.map do |cpk|
|
300
300
|
cpk.map do |pk|
|
@@ -24,7 +24,7 @@ module Sequel
|
|
24
24
|
# # Make the Album class support input transformers
|
25
25
|
# Album.plugin :input_transformer
|
26
26
|
module InputTransformer
|
27
|
-
def self.apply(model,
|
27
|
+
def self.apply(model, *, &_)
|
28
28
|
model.instance_exec do
|
29
29
|
@input_transformers = {}
|
30
30
|
@skip_input_transformer_columns = {}
|
@@ -158,15 +158,19 @@ module Sequel
|
|
158
158
|
end
|
159
159
|
|
160
160
|
# Filter the objects used when tactical eager loading.
|
161
|
-
# By default, this removes frozen objects and objects that alreayd have the association loaded
|
161
|
+
# By default, this removes frozen objects and objects that alreayd have the association loaded,
|
162
|
+
# as well as objects where the reflection for the association is not the same as the receiver's
|
163
|
+
# reflection for the association.
|
162
164
|
def _filter_tactical_eager_load_objects(opts)
|
163
165
|
objects = defined?(super) ? super : retrieved_with.dup
|
166
|
+
name = opts[:name]
|
164
167
|
if opts[:eager_reload]
|
165
168
|
objects.reject!(&:frozen?)
|
166
169
|
else
|
167
|
-
name = opts[:name]
|
168
170
|
objects.reject!{|x| x.frozen? || x.associations.include?(name)}
|
169
171
|
end
|
172
|
+
reflection = self.class.association_reflection(name)
|
173
|
+
objects.select!{|x| x.class.association_reflection(name).equal?(reflection)}
|
170
174
|
objects
|
171
175
|
end
|
172
176
|
end
|
@@ -53,7 +53,7 @@ module Sequel
|
|
53
53
|
def validate_associated_object(reflection, obj)
|
54
54
|
return if reflection[:validate] == false
|
55
55
|
association = reflection[:name]
|
56
|
-
if (reflection[:type] == :one_to_many || reflection[:type] == :one_to_one) && (key = reflection[:key]).is_a?(Symbol) && !(
|
56
|
+
if (reflection[:type] == :one_to_many || reflection[:type] == :one_to_one) && (key = reflection[:key]).is_a?(Symbol) && !(obj.values[key])
|
57
57
|
p_key = pk unless pk.is_a?(Array)
|
58
58
|
if p_key
|
59
59
|
obj.values[key] = p_key
|
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 = 82
|
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.82.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-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -228,6 +228,8 @@ extra_rdoc_files:
|
|
228
228
|
- doc/release_notes/5.79.0.txt
|
229
229
|
- doc/release_notes/5.8.0.txt
|
230
230
|
- doc/release_notes/5.80.0.txt
|
231
|
+
- doc/release_notes/5.81.0.txt
|
232
|
+
- doc/release_notes/5.82.0.txt
|
231
233
|
- doc/release_notes/5.9.0.txt
|
232
234
|
files:
|
233
235
|
- CHANGELOG
|
@@ -336,6 +338,8 @@ files:
|
|
336
338
|
- doc/release_notes/5.79.0.txt
|
337
339
|
- doc/release_notes/5.8.0.txt
|
338
340
|
- doc/release_notes/5.80.0.txt
|
341
|
+
- doc/release_notes/5.81.0.txt
|
342
|
+
- doc/release_notes/5.82.0.txt
|
339
343
|
- doc/release_notes/5.9.0.txt
|
340
344
|
- doc/schema_modification.rdoc
|
341
345
|
- doc/security.rdoc
|
@@ -522,6 +526,7 @@ files:
|
|
522
526
|
- lib/sequel/extensions/symbol_as.rb
|
523
527
|
- lib/sequel/extensions/symbol_as_refinement.rb
|
524
528
|
- lib/sequel/extensions/synchronize_sql.rb
|
529
|
+
- lib/sequel/extensions/temporarily_release_connection.rb
|
525
530
|
- lib/sequel/extensions/thread_local_timezones.rb
|
526
531
|
- lib/sequel/extensions/to_dot.rb
|
527
532
|
- lib/sequel/extensions/transaction_connection_validator.rb
|