sequel 5.62.0 → 5.64.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 +24 -0
- data/doc/association_basics.rdoc +30 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/access.rb +2 -2
- data/lib/sequel/adapters/shared/mysql.rb +1 -1
- data/lib/sequel/adapters/shared/oracle.rb +5 -5
- data/lib/sequel/adapters/shared/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/shared/sqlite.rb +3 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +5 -2
- data/lib/sequel/database/query.rb +38 -4
- data/lib/sequel/dataset/actions.rb +25 -0
- data/lib/sequel/extensions/async_thread_pool.rb +5 -5
- data/lib/sequel/extensions/named_timezones.rb +6 -2
- data/lib/sequel/model/associations.rb +7 -2
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1971882f6efac487f21b4b5fb74695b757f2e129c14d82ffdca062cd5bc9d58f
|
4
|
+
data.tar.gz: ca4d9df4e18bd2806ad0a288856cb87f270f702a73f8de0ebea3934776d4304c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abdd0fbcb5da74256493a8cfb7ff32f9247d8b021096344d3d072d2d5981fec557ed222df11da3db12abae7318c43c09b9bb2c63795816244aea76425f4106ba
|
7
|
+
data.tar.gz: 7b37c7c8cdc9172baf273ee80b328fa79bd6694a070ef81220e31bbaa20bcb0dc90a137b5cccd4c0326a7b99f17dd57591deab375dbaf61881bac3536b4eeb12
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 5.64.0 (2023-01-01)
|
2
|
+
|
3
|
+
* Make :db_type column schema entries on SQLAnywhere include precision/scale information (jeremyevans)
|
4
|
+
|
5
|
+
* Include :min_value and :max_value schema entries for decimal/numeric columns on most databases (rolftimmermans, jeremyevans) (#1975)
|
6
|
+
|
7
|
+
* Support :graph_use_association_block association option to make eager_graph use the association block (jeremyevans)
|
8
|
+
|
9
|
+
* Make many_through_many and many_through_one associations support eager_graph callbacks (jeremyevans)
|
10
|
+
|
11
|
+
=== 5.63.0 (2022-12-01)
|
12
|
+
|
13
|
+
* Make validates_associated plugin avoid database type errors for non-integer association keys (jeremyevans) (#1968)
|
14
|
+
|
15
|
+
* Make tactical_eager_loading plugin work better with table inheritance plugins (rolftimmermans, jeremyevans) (#1962)
|
16
|
+
|
17
|
+
* Add support for pool_class: :timed_queue on Ruby 3.2+, using a Queue for available connections (jeremyevans)
|
18
|
+
|
19
|
+
* Allow :pool_class Database option to be specified as a string to more easily choose a different pool type (jeremyevans)
|
20
|
+
|
21
|
+
* Use compare_by_identity hashes for Thread-keyed hashes in threaded connection pools (jeremyevans)
|
22
|
+
|
23
|
+
* Skip use of JRuby workaround on JRuby 9.3.9.0+ in named_timezones extension as JRuby fixed the related bug (jeremyevans)
|
24
|
+
|
1
25
|
=== 5.62.0 (2022-11-01)
|
2
26
|
|
3
27
|
* Add back the pg_auto_parameterize extension for automatically using bound variables when using postgres adapter with pg driver (jeremyevans)
|
data/doc/association_basics.rdoc
CHANGED
@@ -1498,6 +1498,36 @@ as the qualifiers may not match the aliases automatically used by eager_graph.
|
|
1498
1498
|
This should contain unqualified identifiers, and eager_graph will automatically
|
1499
1499
|
qualify them with the appropriate alias.
|
1500
1500
|
|
1501
|
+
==== :graph_use_association_block
|
1502
|
+
|
1503
|
+
Setting this to true makes eager_graph apply the association block to the
|
1504
|
+
associated dataset before graphing the associated dataset into the receiver.
|
1505
|
+
In most cases when this option is used and the association has a block, the
|
1506
|
+
dataset returned by eager_graph will contain a JOIN to a subquery.
|
1507
|
+
|
1508
|
+
By default (when this option is not used), the association block will be ignored
|
1509
|
+
when using eager_graph:
|
1510
|
+
|
1511
|
+
Artist.one_to_many :tracks do |ds|
|
1512
|
+
ds.where(foo: 3)
|
1513
|
+
end
|
1514
|
+
Artist.eager_graph(:tracks)
|
1515
|
+
# SELECT albums.id, tracks.id AS tracks_id, tracks.album_id
|
1516
|
+
# FROM albums
|
1517
|
+
# LEFT OUTER JOIN tracks
|
1518
|
+
# ON (tracks.album_id = albums.id)
|
1519
|
+
|
1520
|
+
When this option is used, the block will be respected:
|
1521
|
+
|
1522
|
+
Artist.one_to_many :tracks, graph_use_association_block: true do |ds|
|
1523
|
+
ds.where(foo: 3)
|
1524
|
+
end
|
1525
|
+
Artist.eager_graph(:tracks)
|
1526
|
+
# SELECT albums.id, tracks.id AS tracks_id, tracks.album_id
|
1527
|
+
# FROM albums
|
1528
|
+
# LEFT OUTER JOIN (SELECT * FROM tracks WHERE (foo = 3)) AS tracks
|
1529
|
+
# ON (tracks.album_id = albums.id)
|
1530
|
+
|
1501
1531
|
==== :graph_join_table_conditions [+many_to_many+, +one_through_one+]
|
1502
1532
|
|
1503
1533
|
The additional conditions to use on the SQL join for the join table when
|
@@ -0,0 +1,33 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* On Ruby 3.2, the pool_class: :timed_queue Database option can now
|
4
|
+
be used to use an alternative connection pool that stores
|
5
|
+
connections in a queue, and uses the new Queue#pop :timeout option
|
6
|
+
in Ruby 3.2 to implement the pool timeout. This new connection
|
7
|
+
pool is simpler than the default connection pool. It is not yet
|
8
|
+
the default connection pool on Ruby 3.2, but it may become the
|
9
|
+
default in a later version. Users of Ruby 3.2 are encouraged to
|
10
|
+
try out the pool_class: :timed_queue Database option and provide
|
11
|
+
feedback on how it works in their application.
|
12
|
+
|
13
|
+
= Other Improvements
|
14
|
+
|
15
|
+
* The tactical_eager_loading plugin now works in combination with the
|
16
|
+
single_table_inheritance and class_table_inheritance plugins, when
|
17
|
+
loading an association only defined in a specific subclass.
|
18
|
+
Previously, eager loading would be skipped in such a case. Now,
|
19
|
+
an eager load will be attempted for all instances supporting the
|
20
|
+
association.
|
21
|
+
|
22
|
+
* The validate_associated plugin now avoids database type errors for
|
23
|
+
non-integer association keys. In cases where the associated object
|
24
|
+
doesn't have a value for the associated key, and the current object
|
25
|
+
does not have a key value that can be set in the associated object,
|
26
|
+
validation errors in the associated object related to the associated
|
27
|
+
key will be ignored.
|
28
|
+
|
29
|
+
* Thread-keyed connection pool hashes now use compare_by_identity for
|
30
|
+
better performance.
|
31
|
+
|
32
|
+
* The JRuby workaround in the named_timezones extension is no longer
|
33
|
+
used on JRuby 9.3.9.0+, as JRuby fixed the related bug.
|
@@ -0,0 +1,50 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A :graph_use_association_block association option has been added,
|
4
|
+
which makes eager_graph use the association block (as eager does),
|
5
|
+
generally resulting in a JOIN to a subquery:
|
6
|
+
|
7
|
+
Artist.one_to_many :tracks, graph_use_association_block: true do |ds|
|
8
|
+
ds.where(foo: 3)
|
9
|
+
end
|
10
|
+
Artist.eager_graph(:tracks)
|
11
|
+
# SELECT albums.id, tracks.id AS tracks_id, tracks.album_id
|
12
|
+
# FROM albums
|
13
|
+
# LEFT OUTER JOIN (SELECT * FROM tracks WHERE (foo = 3)) AS tracks
|
14
|
+
# ON (tracks.album_id = albums.id)
|
15
|
+
|
16
|
+
Assuming that the database can optimize the query correctly, using
|
17
|
+
the :graph_use_association_block option is probably simpler than
|
18
|
+
than using other :graph_* options to duplicate the conditions added
|
19
|
+
by the association block.
|
20
|
+
|
21
|
+
* Numeric/Decimal column schema entries now include :min_value and
|
22
|
+
:max_value entries on most databases, indicating the minimum and
|
23
|
+
maximum values supported for the column. Similar to the support
|
24
|
+
for integer columns added in 5.62.0, this allows the
|
25
|
+
auto_validations plugin to automatically validate the values of
|
26
|
+
the columns are in the allowed range.
|
27
|
+
|
28
|
+
= Other Improvements
|
29
|
+
|
30
|
+
* many_through_{one,many} associations now support eager_graph
|
31
|
+
callbacks.
|
32
|
+
|
33
|
+
* The :db_type column schema entries on SQLAnywhere now include
|
34
|
+
precision/scale information, to work with the numeric/decimal
|
35
|
+
column min_value/max_value support.
|
36
|
+
|
37
|
+
* The oracle adapter now includes a :column_size column schema
|
38
|
+
entry containing the precision of the columns, to work with the
|
39
|
+
numeric/decimal column min_value/max_value support.
|
40
|
+
|
41
|
+
= Backwards Compatibility
|
42
|
+
|
43
|
+
* The private Database#column_schema_integer_min_max_values method
|
44
|
+
added in 5.62.0 now takes a column schema hash instead of a
|
45
|
+
database type string.
|
46
|
+
|
47
|
+
* Code that previously looked at the :db_type column schema entry on
|
48
|
+
SQLAnywhere should be updated to look at the :domain_name entry, and
|
49
|
+
code that looked at the :domain_name_with_size entry should be
|
50
|
+
updated to look at the :db_type entry.
|
@@ -312,6 +312,7 @@ module Sequel
|
|
312
312
|
:char_used => column.char_used?,
|
313
313
|
:char_size => column.char_size,
|
314
314
|
:data_size => column.data_size,
|
315
|
+
:column_size => column.precision,
|
315
316
|
:precision => column.precision,
|
316
317
|
:scale => column.scale,
|
317
318
|
:fsprecision => column.fsprecision,
|
@@ -60,8 +60,8 @@ module Sequel
|
|
60
60
|
# Access's Byte type will accept much larger values,
|
61
61
|
# even though it only stores 0-255. Do not set min/max
|
62
62
|
# values for the Byte type.
|
63
|
-
def column_schema_integer_min_max_values(
|
64
|
-
return if /byte/i =~ db_type
|
63
|
+
def column_schema_integer_min_max_values(column)
|
64
|
+
return if /byte/i =~ column[:db_type]
|
65
65
|
super
|
66
66
|
end
|
67
67
|
|
@@ -553,7 +553,7 @@ module Sequel
|
|
553
553
|
# Return nil if CHECK constraints are not supported, because
|
554
554
|
# versions that don't support check constraints don't raise
|
555
555
|
# errors for values outside of range.
|
556
|
-
def column_schema_integer_min_max_values(
|
556
|
+
def column_schema_integer_min_max_values(column)
|
557
557
|
super if supports_check_constraints?
|
558
558
|
end
|
559
559
|
|
@@ -178,11 +178,11 @@ module Sequel
|
|
178
178
|
''
|
179
179
|
end
|
180
180
|
|
181
|
-
#
|
182
|
-
#
|
183
|
-
#
|
184
|
-
def column_schema_integer_min_max_values(
|
185
|
-
|
181
|
+
# Support min/max integer values on Oracle only if
|
182
|
+
# they use a NUMBER column with a fixed precision
|
183
|
+
# and no scale.
|
184
|
+
def column_schema_integer_min_max_values(column)
|
185
|
+
super if column[:db_type] =~ /NUMBER\(\d+\)/i || (column[:db_type] == 'NUMBER' && column[:column_size].is_a?(Integer) && column[:scale] == 0)
|
186
186
|
end
|
187
187
|
|
188
188
|
def create_sequence_sql(name, opts=OPTS)
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
row[:auto_increment] = auto_increment == 1 || auto_increment == true
|
38
38
|
row[:primary_key] = row.delete(:pkey) == 'Y'
|
39
39
|
row[:allow_null] = row[:nulls_allowed].is_a?(Integer) ? row.delete(:nulls_allowed) == 1 : row.delete(:nulls_allowed)
|
40
|
-
row[:db_type] = row.delete(:
|
40
|
+
row[:db_type] = row.delete(:domain_name_with_size)
|
41
41
|
row[:type] = if row[:db_type] =~ /numeric/i and (row[:scale].is_a?(Integer) ? row[:scale] == 0 : !row[:scale])
|
42
42
|
:integer
|
43
43
|
else
|
@@ -320,10 +320,11 @@ module Sequel
|
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
323
|
-
# SQLite does not restrict the integer type to a specific range.
|
324
|
-
def column_schema_integer_min_max_values(
|
323
|
+
# SQLite does not restrict the integer or decimal type to a specific range.
|
324
|
+
def column_schema_integer_min_max_values(column)
|
325
325
|
nil
|
326
326
|
end
|
327
|
+
alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
|
327
328
|
|
328
329
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
329
330
|
# new connections.
|
@@ -22,6 +22,8 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
22
22
|
@connections_to_disconnect = []
|
23
23
|
@servers = opts.fetch(:servers_hash, Hash.new(:default))
|
24
24
|
remove_instance_variable(:@waiter)
|
25
|
+
remove_instance_variable(:@allocated)
|
26
|
+
@allocated = {}
|
25
27
|
@waiters = {}
|
26
28
|
|
27
29
|
add_servers([:default])
|
@@ -36,7 +38,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
36
38
|
unless @servers.has_key?(server)
|
37
39
|
@servers[server] = server
|
38
40
|
@available_connections[server] = []
|
39
|
-
|
41
|
+
allocated = {}
|
42
|
+
allocated.compare_by_identity
|
43
|
+
@allocated[server] = allocated
|
40
44
|
@waiters[server] = ConditionVariable.new
|
41
45
|
end
|
42
46
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# A connection pool allowing multi-threaded access to a pool of connections.
|
4
4
|
# This is the default connection pool used by Sequel.
|
5
5
|
class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
6
|
-
USE_WAITER = true
|
6
|
+
USE_WAITER = true # SEQUEL6: Remove
|
7
7
|
Sequel::Deprecation.deprecate_constant(self, :USE_WAITER)
|
8
8
|
|
9
9
|
# The maximum number of connections this pool will create (per shard/server
|
@@ -12,17 +12,17 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
12
12
|
|
13
13
|
# An array of connections that are available for use by the pool.
|
14
14
|
# The calling code should already have the mutex before calling this.
|
15
|
-
attr_reader :available_connections
|
15
|
+
attr_reader :available_connections # SEQUEL6: Remove
|
16
16
|
|
17
|
-
# A hash with thread keys and connection values for currently allocated connections.
|
17
|
+
# A hash with thread/fiber keys and connection values for currently allocated connections.
|
18
18
|
# The calling code should already have the mutex before calling this.
|
19
|
-
attr_reader :allocated
|
19
|
+
attr_reader :allocated # SEQUEL6: Remove
|
20
20
|
|
21
21
|
# The following additional options are respected:
|
22
22
|
# :max_connections :: The maximum number of connections the connection pool
|
23
23
|
# will open (default 4)
|
24
24
|
# :pool_timeout :: The amount of seconds to wait to acquire a connection
|
25
|
-
# before raising a
|
25
|
+
# before raising a PoolTimeout error (default 5)
|
26
26
|
def initialize(db, opts = OPTS)
|
27
27
|
super
|
28
28
|
@max_size = Integer(opts[:max_connections] || 4)
|
@@ -31,6 +31,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
31
31
|
@connection_handling = opts[:connection_handling]
|
32
32
|
@available_connections = []
|
33
33
|
@allocated = {}
|
34
|
+
@allocated.compare_by_identity
|
34
35
|
@timeout = Float(opts[:pool_timeout] || 5)
|
35
36
|
@waiter = ConditionVariable.new
|
36
37
|
end
|
@@ -49,8 +50,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
|
-
# Removes all connections currently available
|
53
|
-
# yielding each connection to the given block. This method has the effect of
|
53
|
+
# Removes all connections currently available. This method has the effect of
|
54
54
|
# disconnecting from the database, assuming that no connections are currently
|
55
55
|
# being used. If you want to be able to disconnect connections that are
|
56
56
|
# currently in use, use the ShardedThreadedConnectionPool, which can do that.
|
@@ -134,7 +134,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
134
134
|
# calling this.
|
135
135
|
#
|
136
136
|
# This should return a connection is one is available within the timeout,
|
137
|
-
# or
|
137
|
+
# or raise PoolTimeout if a connection could not be acquired within the timeout.
|
138
138
|
def acquire(thread)
|
139
139
|
if conn = assign_connection(thread)
|
140
140
|
return conn
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
raise LoadError, "Sequel::TimedQueueConnectionPool is only available on Ruby 3.2+" unless RUBY_VERSION >= '3.2'
|
5
|
+
# :nocov:
|
6
|
+
|
7
|
+
# A connection pool allowing multi-threaded access to a pool of connections,
|
8
|
+
# using a timed queue (only available in Ruby 3.2+).
|
9
|
+
class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
|
10
|
+
# The maximum number of connections this pool will create.
|
11
|
+
attr_reader :max_size
|
12
|
+
|
13
|
+
# The following additional options are respected:
|
14
|
+
# :max_connections :: The maximum number of connections the connection pool
|
15
|
+
# will open (default 4)
|
16
|
+
# :pool_timeout :: The amount of seconds to wait to acquire a connection
|
17
|
+
# before raising a PoolTimeout (default 5)
|
18
|
+
def initialize(db, opts = OPTS)
|
19
|
+
super
|
20
|
+
@max_size = Integer(opts[:max_connections] || 4)
|
21
|
+
raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
|
22
|
+
@mutex = Mutex.new
|
23
|
+
# Size inside array so this still works while the pool is frozen.
|
24
|
+
@size = [0]
|
25
|
+
@allocated = {}
|
26
|
+
@allocated.compare_by_identity
|
27
|
+
@timeout = Float(opts[:pool_timeout] || 5)
|
28
|
+
@queue = Queue.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Yield all of the available connections, and the one currently allocated to
|
32
|
+
# this thread. This will not yield connections currently allocated to other
|
33
|
+
# threads, as it is not safe to operate on them.
|
34
|
+
def all_connections
|
35
|
+
hold do |conn|
|
36
|
+
yield conn
|
37
|
+
|
38
|
+
# Use a hash to record all connections already seen. As soon as we
|
39
|
+
# come across a connection we've already seen, we stop the loop.
|
40
|
+
conns = {}
|
41
|
+
conns.compare_by_identity
|
42
|
+
while true
|
43
|
+
conn = nil
|
44
|
+
begin
|
45
|
+
break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
|
46
|
+
conns[conn] = true
|
47
|
+
yield conn
|
48
|
+
ensure
|
49
|
+
@queue.push(conn) if conn
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Removes all connections currently in the pool's queue. This method has the effect of
|
56
|
+
# disconnecting from the database, assuming that no connections are currently
|
57
|
+
# being used.
|
58
|
+
#
|
59
|
+
# Once a connection is requested using #hold, the connection pool
|
60
|
+
# creates new connections to the database.
|
61
|
+
def disconnect(opts=OPTS)
|
62
|
+
while conn = @queue.pop(timeout: 0)
|
63
|
+
disconnect_connection(conn)
|
64
|
+
end
|
65
|
+
fill_queue
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Chooses the first available connection, or if none are
|
70
|
+
# available, creates a new connection. Passes the connection to the supplied
|
71
|
+
# block:
|
72
|
+
#
|
73
|
+
# pool.hold {|conn| conn.execute('DROP TABLE posts')}
|
74
|
+
#
|
75
|
+
# Pool#hold is re-entrant, meaning it can be called recursively in
|
76
|
+
# the same thread without blocking.
|
77
|
+
#
|
78
|
+
# If no connection is immediately available and the pool is already using the maximum
|
79
|
+
# number of connections, Pool#hold will block until a connection
|
80
|
+
# is available or the timeout expires. If the timeout expires before a
|
81
|
+
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
82
|
+
def hold(server=nil)
|
83
|
+
t = Sequel.current
|
84
|
+
if conn = sync{@allocated[t]}
|
85
|
+
return yield(conn)
|
86
|
+
end
|
87
|
+
|
88
|
+
begin
|
89
|
+
conn = acquire(t)
|
90
|
+
yield conn
|
91
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
92
|
+
if disconnect_error?(e)
|
93
|
+
oconn = conn
|
94
|
+
conn = nil
|
95
|
+
disconnect_connection(oconn) if oconn
|
96
|
+
sync{@allocated.delete(t)}
|
97
|
+
fill_queue
|
98
|
+
end
|
99
|
+
raise
|
100
|
+
ensure
|
101
|
+
release(t) if conn
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def pool_type
|
106
|
+
:timed_queue
|
107
|
+
end
|
108
|
+
|
109
|
+
# The total number of connections in the pool.
|
110
|
+
def size
|
111
|
+
sync{@size[0]}
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
# Create a new connection, after the pool's current size has already
|
117
|
+
# been updated to account for the new connection. If there is an exception
|
118
|
+
# when creating the connection, decrement the current size.
|
119
|
+
#
|
120
|
+
# This should only be called after can_make_new?. If there is an exception
|
121
|
+
# between when can_make_new? is called and when preallocated_make_new
|
122
|
+
# is called, it has the effect of reducing the maximum size of the
|
123
|
+
# connection pool by 1, since the current size of the pool will show a
|
124
|
+
# higher number than the number of connections allocated or
|
125
|
+
# in the queue.
|
126
|
+
#
|
127
|
+
# Calling code should not have the mutex when calling this.
|
128
|
+
def preallocated_make_new
|
129
|
+
make_new(:default)
|
130
|
+
rescue Exception
|
131
|
+
sync{@size[0] -= 1}
|
132
|
+
raise
|
133
|
+
end
|
134
|
+
|
135
|
+
# Decrement the current size of the pool when disconnecting connections.
|
136
|
+
#
|
137
|
+
# Calling code should not have the mutex when calling this.
|
138
|
+
def disconnect_connection(conn)
|
139
|
+
sync{@size[0] -= 1}
|
140
|
+
super
|
141
|
+
end
|
142
|
+
|
143
|
+
# If there are any threads waiting on the queue, try to create
|
144
|
+
# new connections in a separate thread if the pool is not yet at the
|
145
|
+
# maximum size.
|
146
|
+
#
|
147
|
+
# The reason for this method is to handle cases where acquire
|
148
|
+
# could not retrieve a connection immediately, and the pool
|
149
|
+
# was already at the maximum size. In that case, the acquire will
|
150
|
+
# wait on the queue until the timeout. This method is called
|
151
|
+
# after disconnecting to potentially add new connections to the
|
152
|
+
# pool, so the threads that are currently waiting for connections
|
153
|
+
# do not timeout after the pool is no longer full.
|
154
|
+
def fill_queue
|
155
|
+
if @queue.num_waiting > 0
|
156
|
+
Thread.new do
|
157
|
+
while @queue.num_waiting > 0 && (conn = try_make_new)
|
158
|
+
@queue.push(conn)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Whether the given size is less than the maximum size of the pool.
|
165
|
+
# In that case, the pool's current size is incremented. If this
|
166
|
+
# method returns true, space in the pool for the connection is
|
167
|
+
# preallocated, and preallocated_make_new should be called to
|
168
|
+
# create the connection.
|
169
|
+
#
|
170
|
+
# Calling code should have the mutex when calling this.
|
171
|
+
def can_make_new?(current_size)
|
172
|
+
if @max_size > current_size
|
173
|
+
@size[0] += 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Try to make a new connection if there is space in the pool.
|
178
|
+
# If the pool is already full, look for dead threads/fibers and
|
179
|
+
# disconnect the related connections.
|
180
|
+
#
|
181
|
+
# Calling code should not have the mutex when calling this.
|
182
|
+
def try_make_new
|
183
|
+
return preallocated_make_new if sync{can_make_new?(@size[0])}
|
184
|
+
|
185
|
+
to_disconnect = nil
|
186
|
+
do_make_new = false
|
187
|
+
|
188
|
+
sync do
|
189
|
+
current_size = @size[0]
|
190
|
+
@allocated.keys.each do |t|
|
191
|
+
unless t.alive?
|
192
|
+
(to_disconnect ||= []) << @allocated.delete(t)
|
193
|
+
current_size -= 1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
do_make_new = true if can_make_new?(current_size)
|
198
|
+
end
|
199
|
+
|
200
|
+
begin
|
201
|
+
preallocated_make_new if do_make_new
|
202
|
+
ensure
|
203
|
+
if to_disconnect
|
204
|
+
to_disconnect.each{|conn| disconnect_connection(conn)}
|
205
|
+
fill_queue
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Assigns a connection to the supplied thread, if one
|
211
|
+
# is available.
|
212
|
+
#
|
213
|
+
# This should return a connection is one is available within the timeout,
|
214
|
+
# or raise PoolTimeout if a connection could not be acquired within the timeout.
|
215
|
+
#
|
216
|
+
# Calling code should not have the mutex when calling this.
|
217
|
+
def acquire(thread)
|
218
|
+
if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
|
219
|
+
sync{@allocated[thread] = conn}
|
220
|
+
else
|
221
|
+
name = db.opts[:name]
|
222
|
+
raise ::Sequel::PoolTimeout, "timeout: #{@timeout}#{", database name: #{name}" if name}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Create the maximum number of connections immediately. This should not be called
|
227
|
+
# with a true argument unles no code is currently operating on the database.
|
228
|
+
#
|
229
|
+
# Calling code should not have the mutex when calling this.
|
230
|
+
def preconnect(concurrent = false)
|
231
|
+
if concurrent
|
232
|
+
if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
|
233
|
+
times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
|
234
|
+
end
|
235
|
+
else
|
236
|
+
while conn = try_make_new
|
237
|
+
@queue.push(conn)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
nil
|
242
|
+
end
|
243
|
+
|
244
|
+
# Releases the connection assigned to the supplied thread back to the pool.
|
245
|
+
#
|
246
|
+
# Calling code should not have the mutex when calling this.
|
247
|
+
def release(thread)
|
248
|
+
@queue.push(sync{@allocated.delete(thread)})
|
249
|
+
end
|
250
|
+
|
251
|
+
# Yield to the block while inside the mutex.
|
252
|
+
#
|
253
|
+
# Calling code should not have the mutex when calling this.
|
254
|
+
def sync
|
255
|
+
@mutex.synchronize{yield}
|
256
|
+
end
|
257
|
+
end
|
@@ -30,8 +30,11 @@ class Sequel::ConnectionPool
|
|
30
30
|
:threaded => :ThreadedConnectionPool,
|
31
31
|
:single => :SingleConnectionPool,
|
32
32
|
:sharded_threaded => :ShardedThreadedConnectionPool,
|
33
|
-
:sharded_single => :ShardedSingleConnectionPool
|
34
|
-
|
33
|
+
:sharded_single => :ShardedSingleConnectionPool,
|
34
|
+
:timed_queue => :TimedQueueConnectionPool,
|
35
|
+
}
|
36
|
+
POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
|
37
|
+
POOL_CLASS_MAP.freeze
|
35
38
|
|
36
39
|
# Class methods used to return an appropriate pool subclass, separated
|
37
40
|
# into a module for easier overridding by extensions.
|
@@ -175,8 +175,14 @@ module Sequel
|
|
175
175
|
if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
|
176
176
|
c[:max_length] = max_length
|
177
177
|
end
|
178
|
-
if !c[:max_value] && !c[:min_value]
|
179
|
-
|
178
|
+
if !c[:max_value] && !c[:min_value]
|
179
|
+
min_max = case c[:type]
|
180
|
+
when :integer
|
181
|
+
column_schema_integer_min_max_values(c)
|
182
|
+
when :decimal
|
183
|
+
column_schema_decimal_min_max_values(c)
|
184
|
+
end
|
185
|
+
c[:min_value], c[:max_value] = min_max if min_max
|
180
186
|
end
|
181
187
|
end
|
182
188
|
schema_post_process(cols)
|
@@ -288,7 +294,15 @@ module Sequel
|
|
288
294
|
|
289
295
|
# Look at the db_type and guess the minimum and maximum integer values for
|
290
296
|
# the column.
|
291
|
-
def column_schema_integer_min_max_values(
|
297
|
+
def column_schema_integer_min_max_values(column)
|
298
|
+
db_type = column[:db_type]
|
299
|
+
if /decimal|numeric|number/i =~ db_type
|
300
|
+
if min_max = column_schema_decimal_min_max_values(column)
|
301
|
+
min_max.map!(&:to_i)
|
302
|
+
end
|
303
|
+
return min_max
|
304
|
+
end
|
305
|
+
|
292
306
|
unsigned = /unsigned/i =~ db_type
|
293
307
|
case db_type
|
294
308
|
when /big|int8/i
|
@@ -304,6 +318,26 @@ module Sequel
|
|
304
318
|
end
|
305
319
|
end
|
306
320
|
|
321
|
+
# Look at the db_type and guess the minimum and maximum decimal values for
|
322
|
+
# the column.
|
323
|
+
def column_schema_decimal_min_max_values(column)
|
324
|
+
if column[:column_size] && column[:scale]
|
325
|
+
precision = column[:column_size]
|
326
|
+
scale = column[:scale]
|
327
|
+
elsif /\((\d+)(?:,\s*(-?\d+))?\)/ =~ column[:db_type]
|
328
|
+
precision = $1.to_i
|
329
|
+
scale = $2.to_i if $2
|
330
|
+
end
|
331
|
+
|
332
|
+
if precision
|
333
|
+
limit = BigDecimal("9" * precision)
|
334
|
+
if scale
|
335
|
+
limit /= 10**(scale)
|
336
|
+
end
|
337
|
+
[-limit, limit]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
307
341
|
# Whether the tinyint type (if supported by the database) is unsigned by default.
|
308
342
|
def column_schema_tinyint_type_is_unsigned?
|
309
343
|
false
|
@@ -370,7 +404,7 @@ module Sequel
|
|
370
404
|
:boolean
|
371
405
|
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
|
372
406
|
:float
|
373
|
-
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(
|
407
|
+
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/io
|
374
408
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
375
409
|
when /bytea|blob|image|(var)?binary/io
|
376
410
|
:blob
|
@@ -127,6 +127,18 @@ module Sequel
|
|
127
127
|
#
|
128
128
|
# DB[:table].delete # DELETE * FROM table
|
129
129
|
# # => 3
|
130
|
+
#
|
131
|
+
# Some databases support using multiple tables in a DELETE query. This requires
|
132
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
133
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
134
|
+
# appropriate filters for the FROM tables:
|
135
|
+
#
|
136
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
|
137
|
+
# delete
|
138
|
+
# # DELETE FROM a
|
139
|
+
# # USING b
|
140
|
+
# # INNER JOIN c ON (c.d = b.e)
|
141
|
+
# # WHERE ((a.f = b.g) AND (a.id = c.h))
|
130
142
|
def delete(&block)
|
131
143
|
sql = delete_sql
|
132
144
|
if uses_returning?(:delete)
|
@@ -926,6 +938,19 @@ module Sequel
|
|
926
938
|
#
|
927
939
|
# DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
|
928
940
|
# # => 10
|
941
|
+
#
|
942
|
+
# Some databases support using multiple tables in an UPDATE query. This requires
|
943
|
+
# multiple FROM tables (JOINs can also be used). As multiple FROM tables use
|
944
|
+
# an implicit CROSS JOIN, you should make sure your WHERE condition uses the
|
945
|
+
# appropriate filters for the FROM tables:
|
946
|
+
#
|
947
|
+
# DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
|
948
|
+
# update(:f=>Sequel[:c][:h])
|
949
|
+
# # UPDATE a
|
950
|
+
# # SET f = c.h
|
951
|
+
# # FROM b
|
952
|
+
# # INNER JOIN c ON (c.d = b.e)
|
953
|
+
# # WHERE ((a.f = b.g) AND (a.id = 10))
|
929
954
|
def update(values=OPTS, &block)
|
930
955
|
sql = update_sql(values)
|
931
956
|
if uses_returning?(:update)
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# code
|
6
6
|
#
|
7
7
|
# DB.extension :async_thread_pool
|
8
|
-
# foos = DB[:foos].async.where
|
8
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
9
9
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
10
10
|
# baz_1 = DB[:bazes].async.first(id: 1)
|
11
11
|
#
|
@@ -15,7 +15,7 @@
|
|
15
15
|
# of calling that method on the result of the query method. For example,
|
16
16
|
# if you run:
|
17
17
|
#
|
18
|
-
# foos = DB[:foos].async.where
|
18
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
19
19
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
20
20
|
# baz_1 = DB[:bazes].async.first(id: 1)
|
21
21
|
# sleep(1)
|
@@ -26,7 +26,7 @@
|
|
26
26
|
# These three queries will generally be run concurrently in separate
|
27
27
|
# threads. If you instead run:
|
28
28
|
#
|
29
|
-
# DB[:foos].async.where
|
29
|
+
# DB[:foos].async.where(name: 'A'..'M').all.size
|
30
30
|
# DB[:bars].async.select_order_map(:name).first
|
31
31
|
# DB[:bazes].async.first(id: 1).name
|
32
32
|
#
|
@@ -37,7 +37,7 @@
|
|
37
37
|
# What is run in the separate thread is the entire method call that
|
38
38
|
# returns results. So with the original example:
|
39
39
|
#
|
40
|
-
# foos = DB[:foos].async.where
|
40
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
41
41
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
42
42
|
# baz_1 = DB[:bazes].async.first(id: 1)
|
43
43
|
#
|
@@ -156,7 +156,7 @@
|
|
156
156
|
# so that the query will run in the current thread instead of waiting
|
157
157
|
# for an async thread to become available. With the following code:
|
158
158
|
#
|
159
|
-
# foos = DB[:foos].async.where
|
159
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
160
160
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
161
161
|
# if foos.length > 4
|
162
162
|
# baz_1 = DB[:bazes].async.first(id: 1)
|
@@ -68,6 +68,10 @@ module Sequel
|
|
68
68
|
private
|
69
69
|
|
70
70
|
if RUBY_VERSION >= '2.6'
|
71
|
+
# Whether Time.at with :nsec and :in is broken. True on JRuby < 9.3.9.0.
|
72
|
+
BROKEN_TIME_AT_WITH_NSEC = defined?(JRUBY_VERSION) && (JRUBY_VERSION < '9.3' || (JRUBY_VERSION < '9.4' && JRUBY_VERSION.split('.')[2].to_i < 9))
|
73
|
+
private_constant :BROKEN_TIME_AT_WITH_NSEC
|
74
|
+
|
71
75
|
# Convert the given input Time (which must be in UTC) to the given input timezone,
|
72
76
|
# which should be a TZInfo::Timezone instance.
|
73
77
|
def convert_input_time_other(v, input_timezone)
|
@@ -77,7 +81,7 @@ module Sequel
|
|
77
81
|
period = input_timezone.period_for_local(v, &disamb)
|
78
82
|
offset = period.utc_total_offset
|
79
83
|
# :nocov:
|
80
|
-
if
|
84
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
81
85
|
Time.at(v.to_i - offset, :in => input_timezone) + v.nsec/1000000000.0
|
82
86
|
# :nocov:
|
83
87
|
else
|
@@ -89,7 +93,7 @@ module Sequel
|
|
89
93
|
# which should be a TZInfo::Timezone instance.
|
90
94
|
def convert_output_time_other(v, output_timezone)
|
91
95
|
# :nocov:
|
92
|
-
if
|
96
|
+
if BROKEN_TIME_AT_WITH_NSEC
|
93
97
|
Time.at(v.to_i, :in => output_timezone) + v.nsec/1000000000.0
|
94
98
|
# :nocov:
|
95
99
|
else
|
@@ -1722,6 +1722,8 @@ module Sequel
|
|
1722
1722
|
# :graph_select :: A column or array of columns to select from the associated table
|
1723
1723
|
# when eagerly loading the association via +eager_graph+. Defaults to all
|
1724
1724
|
# columns in the associated table.
|
1725
|
+
# :graph_use_association_block :: Makes eager_graph consider the association block. Without this, eager_graph
|
1726
|
+
# ignores the bock and only use the :graph_* options.
|
1725
1727
|
# :instance_specific :: Marks the association as instance specific. Should be used if the association block
|
1726
1728
|
# uses instance specific state, or transient state (accessing current date/time, etc.).
|
1727
1729
|
# :limit :: Limit the number of records to the provided value. Use
|
@@ -2461,6 +2463,9 @@ module Sequel
|
|
2461
2463
|
# Return dataset to graph into given the association reflection, applying the :callback option if set.
|
2462
2464
|
def eager_graph_dataset(opts, eager_options)
|
2463
2465
|
ds = opts.associated_class.dataset
|
2466
|
+
if opts[:graph_use_association_block] && (b = opts[:block])
|
2467
|
+
ds = b.call(ds)
|
2468
|
+
end
|
2464
2469
|
if cb = eager_options[:callback]
|
2465
2470
|
ds = cb.call(ds)
|
2466
2471
|
end
|
@@ -3552,11 +3557,11 @@ module Sequel
|
|
3552
3557
|
end
|
3553
3558
|
|
3554
3559
|
# Eagerly load all specified associations.
|
3555
|
-
def eager_load(a, eager_assoc=@opts[:eager])
|
3560
|
+
def eager_load(a, eager_assoc=@opts[:eager], m=model)
|
3556
3561
|
return if a.empty?
|
3557
3562
|
|
3558
3563
|
# Reflections for all associations to eager load
|
3559
|
-
reflections = eager_assoc.keys.map{|assoc|
|
3564
|
+
reflections = eager_assoc.keys.map{|assoc| m.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3560
3565
|
|
3561
3566
|
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
3562
3567
|
|
@@ -385,7 +385,7 @@ module Sequel
|
|
385
385
|
iq = nil
|
386
386
|
end
|
387
387
|
fe = opts.final_edge
|
388
|
-
ds.graph(opts
|
388
|
+
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : (Array(opts.right_primary_key).zip(Array(fe[:left])) + conditions), :select=>select, :table_alias=>eo[:table_alias], :qualify=>:deep, :join_type=>eo[:join_type]||join_type, :join_only=>eo[:join_only], &graph_block)
|
389
389
|
end
|
390
390
|
end
|
391
391
|
end
|
@@ -253,6 +253,14 @@ module Sequel
|
|
253
253
|
|
254
254
|
private
|
255
255
|
|
256
|
+
# Limit tactical eager loading objects to objects that support the same association.
|
257
|
+
def _filter_tactical_eager_load_objects(opts)
|
258
|
+
objects = defined?(super) ? super : retrieved_with.dup
|
259
|
+
name = opts[:name]
|
260
|
+
objects.select!{|x| x.model.association_reflections.include?(name)}
|
261
|
+
objects
|
262
|
+
end
|
263
|
+
|
256
264
|
# Don't allow use of prepared statements.
|
257
265
|
def use_prepared_statements_for?(type)
|
258
266
|
false
|
@@ -152,23 +152,23 @@ module Sequel
|
|
152
152
|
name = opts[:name]
|
153
153
|
eager_reload = dynamic_opts[:eager_reload]
|
154
154
|
if (!associations.include?(name) || eager_reload) && opts[:allow_eager] != false && retrieved_by && !frozen? && !dynamic_opts[:callback] && !dynamic_opts[:reload]
|
155
|
-
|
156
|
-
objects = if eager_reload
|
157
|
-
retrieved_with.reject(&:frozen?)
|
158
|
-
else
|
159
|
-
retrieved_with.reject{|x| x.frozen? || x.associations.include?(name)}
|
160
|
-
end
|
161
|
-
retrieved_by.send(:eager_load, objects, name=>dynamic_opts[:eager] || OPTS)
|
162
|
-
rescue Sequel::UndefinedAssociation
|
163
|
-
# This can happen if class table inheritance is used and the association
|
164
|
-
# is only defined in a subclass. This particular instance can use the
|
165
|
-
# association, but it can't be eagerly loaded as the parent class doesn't
|
166
|
-
# have access to the association, and that's the class doing the eager loading.
|
167
|
-
nil
|
168
|
-
end
|
155
|
+
retrieved_by.send(:eager_load, _filter_tactical_eager_load_objects(:eager_reload=>eager_reload, :name=>name), {name=>dynamic_opts[:eager] || OPTS}, model)
|
169
156
|
end
|
170
157
|
super
|
171
158
|
end
|
159
|
+
|
160
|
+
# Filter the objects used when tactical eager loading.
|
161
|
+
# By default, this removes frozen objects and objects that alreayd have the association loaded
|
162
|
+
def _filter_tactical_eager_load_objects(opts)
|
163
|
+
objects = defined?(super) ? super : retrieved_with.dup
|
164
|
+
if opts[:eager_reload]
|
165
|
+
objects.reject!(&:frozen?)
|
166
|
+
else
|
167
|
+
name = opts[:name]
|
168
|
+
objects.reject!{|x| x.frozen? || x.associations.include?(name)}
|
169
|
+
end
|
170
|
+
objects
|
171
|
+
end
|
172
172
|
end
|
173
173
|
|
174
174
|
module DatasetMethods
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Sequel
|
4
4
|
module Plugins
|
5
|
-
# The
|
5
|
+
# The validate_associated plugin allows you to validate associated
|
6
6
|
# objects. It also offers the ability to delay the validation of
|
7
7
|
# associated objects until the current object is validated.
|
8
8
|
# If the associated object is invalid, validation error messages
|
@@ -54,22 +54,32 @@ module Sequel
|
|
54
54
|
return if reflection[:validate] == false
|
55
55
|
association = reflection[:name]
|
56
56
|
if (reflection[:type] == :one_to_many || reflection[:type] == :one_to_one) && (key = reflection[:key]).is_a?(Symbol) && !(pk_val = obj.values[key])
|
57
|
-
# There could be a presence validation on the foreign key in the associated model,
|
58
|
-
# which will fail if we validate before saving the current object. If there is
|
59
|
-
# no value for the foreign key, set it to the current primary key value, or a dummy
|
60
|
-
# value of 0 if we haven't saved the current object.
|
61
57
|
p_key = pk unless pk.is_a?(Array)
|
62
|
-
|
63
|
-
|
58
|
+
if p_key
|
59
|
+
obj.values[key] = p_key
|
60
|
+
else
|
61
|
+
ignore_key_errors = true
|
62
|
+
end
|
64
63
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
|
65
|
+
unless obj.valid?
|
66
|
+
if ignore_key_errors
|
67
|
+
# Ignore errors on the key column in the associated object. This column
|
68
|
+
# will be set when saving to a presumably valid value using a column
|
69
|
+
# in the current object (which may not be available until after the current
|
70
|
+
# object is saved).
|
71
|
+
obj.errors.delete(key)
|
72
|
+
obj.errors.delete_if{|k,| Array === k && k.include?(key)}
|
73
|
+
end
|
74
|
+
|
75
|
+
obj.errors.full_messages.each do |m|
|
76
|
+
errors.add(association, m)
|
77
|
+
end
|
69
78
|
end
|
79
|
+
|
80
|
+
nil
|
70
81
|
end
|
71
82
|
end
|
72
83
|
end
|
73
84
|
end
|
74
85
|
end
|
75
|
-
|
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 = 64
|
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.64.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:
|
11
|
+
date: 2023-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -194,6 +194,8 @@ extra_rdoc_files:
|
|
194
194
|
- doc/release_notes/5.60.0.txt
|
195
195
|
- doc/release_notes/5.61.0.txt
|
196
196
|
- doc/release_notes/5.62.0.txt
|
197
|
+
- doc/release_notes/5.63.0.txt
|
198
|
+
- doc/release_notes/5.64.0.txt
|
197
199
|
- doc/release_notes/5.7.0.txt
|
198
200
|
- doc/release_notes/5.8.0.txt
|
199
201
|
- doc/release_notes/5.9.0.txt
|
@@ -284,6 +286,8 @@ files:
|
|
284
286
|
- doc/release_notes/5.60.0.txt
|
285
287
|
- doc/release_notes/5.61.0.txt
|
286
288
|
- doc/release_notes/5.62.0.txt
|
289
|
+
- doc/release_notes/5.63.0.txt
|
290
|
+
- doc/release_notes/5.64.0.txt
|
287
291
|
- doc/release_notes/5.7.0.txt
|
288
292
|
- doc/release_notes/5.8.0.txt
|
289
293
|
- doc/release_notes/5.9.0.txt
|
@@ -352,6 +356,7 @@ files:
|
|
352
356
|
- lib/sequel/connection_pool/sharded_threaded.rb
|
353
357
|
- lib/sequel/connection_pool/single.rb
|
354
358
|
- lib/sequel/connection_pool/threaded.rb
|
359
|
+
- lib/sequel/connection_pool/timed_queue.rb
|
355
360
|
- lib/sequel/core.rb
|
356
361
|
- lib/sequel/database.rb
|
357
362
|
- lib/sequel/database/connecting.rb
|
@@ -606,7 +611,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
606
611
|
- !ruby/object:Gem::Version
|
607
612
|
version: '0'
|
608
613
|
requirements: []
|
609
|
-
rubygems_version: 3.
|
614
|
+
rubygems_version: 3.4.1
|
610
615
|
signing_key:
|
611
616
|
specification_version: 4
|
612
617
|
summary: The Database Toolkit for Ruby
|