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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddf0e8e9009f39ca7f6dffca8aef10ef4fbdf22a8c5002def5ecea4042f38763
4
- data.tar.gz: b9f3cb97273dd9885e8002a64e17bddd0a6db2f932d1c68ce97844e2ac843096
3
+ metadata.gz: f09a139a164322e0a0e52e9a58c79b2f463972363509c41bf50e7082c1f542b7
4
+ data.tar.gz: e7222b81a79111b9853565160f81cb18c72b341f18bec839046c9b49a4b199ea
5
5
  SHA512:
6
- metadata.gz: 7387d403f32cb492abe6fad35d6a87a4ac274fc3602aee0f8afba38d9feb30578ee774de2b168a88d86ab740a41fa2a62333f695c0d2fa507fde775943917daa
7
- data.tar.gz: 11b1c1e9daf92153585f158358ee85078b83decfca8bcf4569fa90e59335dfd123a0d8fddf8f6e58e166801029de23937279dc3aa746d0d3878a34dac90ce636
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
- require 'logger'
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
- require 'logger'
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
- The `subclasses` plugin can be used to keep track of all model classes
95
- that have been setup in your application. Finalizing their associations
96
- and freezing them can easily be achieved through the plugin:
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
@@ -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 override = @opts[:override]
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
- # Returning is always supported.
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
- true
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
- sync do
201
- @waiters[server].wait(@mutex, timeout)
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
- sync do
215
- @waiters[server].wait(@mutex, timeout - elapsed)
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
- sync do
147
- @waiter.wait(@mutex, timeout)
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
- sync do
161
- @waiter.wait(@mutex, timeout - elapsed)
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
- if RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.5'
274
- # :nocov:
275
- def synchronize(server=nil)
276
- @pool.hold(server || :default){|conn| yield conn}
277
- end
278
- # :nocov:
279
- else
280
- # Acquires a database connection, yielding it to the passed block. This is
281
- # useful if you want to make sure the same connection is used for all
282
- # database queries in the block. It is also useful if you want to gain
283
- # direct access to the underlying connection object if you need to do
284
- # something Sequel does not natively support.
285
- #
286
- # If a server option is given, acquires a connection for that specific
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
@@ -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? && (convs = cols.map{|c| c[:type] == :integer}).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|
@@ -586,7 +586,7 @@ module Sequel
586
586
  end
587
587
  end
588
588
 
589
- def self.apply(model, opts=OPTS)
589
+ def self.apply(model, opts=OPTS, &_)
590
590
  model.plugin :serialization
591
591
  end
592
592
 
@@ -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) && !(pk_val = obj.values[key])
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
@@ -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 = 80
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.80.0
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-05-01 00:00:00.000000000 Z
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