sequel 5.88.0 → 5.90.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/lib/sequel/adapters/ibmdb.rb +1 -0
- data/lib/sequel/adapters/mysql2.rb +8 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +1 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +15 -4
- data/lib/sequel/core.rb +15 -0
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +5 -1
- data/lib/sequel/database/schema_generator.rb +8 -0
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +9 -4
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +19 -2
- data/lib/sequel/model/base.rb +8 -4
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +1 -1
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/subset_conditions.rb +1 -0
- data/lib/sequel/plugins/subset_static_cache.rb +2 -1
- data/lib/sequel/sql.rb +8 -1
- data/lib/sequel/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba9973675e3b9e358ff96c334052ab6ba73e1db4c87c455163b60e367d0eb568
|
4
|
+
data.tar.gz: 86c4197ba9ced0d6bd3a789e5c20e4d0656bf5baca103f0b6db33e45b7981258
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0247f84faceeb8a8c97d85bdefc3d0b781986e630981c163ca58bf7965f852a472d9e9501c5913ee1f4e7b02c005817c76ae8e71fcb0479fae252b27662e05f7
|
7
|
+
data.tar.gz: f90672de6e1ae901dca735988fa1466647bdc8f0cfeba75c1f27892e14e4eaf316b0141906039c9a33a70150b1860cda1f7a1972858ece6e3752614a91a60f2c
|
@@ -97,7 +97,14 @@ module Sequel
|
|
97
97
|
synchronize(opts[:server]) do |conn|
|
98
98
|
stmt, ps_sql = conn.prepared_statements[ps_name]
|
99
99
|
unless ps_sql == sql
|
100
|
-
|
100
|
+
if stmt
|
101
|
+
begin
|
102
|
+
stmt.close
|
103
|
+
rescue ::Mysql2::Error
|
104
|
+
# probably Invalid statement handle, can happen from dropping
|
105
|
+
# related table, ignore as we won't be using it again.
|
106
|
+
end
|
107
|
+
end
|
101
108
|
stmt = log_connection_yield("Preparing #{ps_name}: #{sql}", conn){conn.prepare(sql)}
|
102
109
|
conn.prepared_statements[ps_name] = [stmt, sql]
|
103
110
|
end
|
@@ -88,6 +88,7 @@ module Sequel
|
|
88
88
|
|
89
89
|
module DatasetMethods
|
90
90
|
include(Module.new do
|
91
|
+
Sequel.set_temp_name(self){"Sequel::Access::DatasetMethods::_SQLMethods"}
|
91
92
|
Dataset.def_sql_method(self, :select, %w'select distinct limit columns into from join where group order having compounds')
|
92
93
|
end)
|
93
94
|
include EmulateOffsetWithReverseAndCount
|
@@ -574,6 +574,7 @@ module Sequel
|
|
574
574
|
|
575
575
|
module DatasetMethods
|
576
576
|
include(Module.new do
|
577
|
+
Sequel.set_temp_name(self){"Sequel::MSSQL::DatasetMethods::_SQLMethods"}
|
577
578
|
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from lock join where group having compounds order')
|
578
579
|
end)
|
579
580
|
include EmulateOffsetWithRowNumber
|
@@ -333,6 +333,7 @@ module Sequel
|
|
333
333
|
BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
|
334
334
|
|
335
335
|
include(Module.new do
|
336
|
+
Sequel.set_temp_name(self){"Sequel::Oracle::DatasetMethods::_SQLMethods"}
|
336
337
|
Dataset.def_sql_method(self, :select, %w'with select distinct columns from join where group having compounds order limit lock')
|
337
338
|
end)
|
338
339
|
|
@@ -1271,21 +1271,32 @@ module Sequel
|
|
1271
1271
|
|
1272
1272
|
# Handle exclusion constraints.
|
1273
1273
|
def constraint_definition_sql(constraint)
|
1274
|
-
case constraint[:type]
|
1274
|
+
case type = constraint[:type]
|
1275
1275
|
when :exclude
|
1276
1276
|
elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
|
1277
1277
|
sql = String.new
|
1278
1278
|
sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
|
1279
1279
|
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
1280
1280
|
sql
|
1281
|
-
when :
|
1281
|
+
when :primary_key, :unique
|
1282
|
+
if using_index = constraint[:using_index]
|
1283
|
+
sql = String.new
|
1284
|
+
sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
|
1285
|
+
if type == :primary_key
|
1286
|
+
sql << primary_key_constraint_sql_fragment(constraint)
|
1287
|
+
else
|
1288
|
+
sql << unique_constraint_sql_fragment(constraint)
|
1289
|
+
end
|
1290
|
+
sql << " USING INDEX " << quote_identifier(using_index)
|
1291
|
+
else
|
1292
|
+
super
|
1293
|
+
end
|
1294
|
+
else # when :foreign_key, :check
|
1282
1295
|
sql = super
|
1283
1296
|
if constraint[:not_valid]
|
1284
1297
|
sql << " NOT VALID"
|
1285
1298
|
end
|
1286
1299
|
sql
|
1287
|
-
else
|
1288
|
-
super
|
1289
1300
|
end
|
1290
1301
|
end
|
1291
1302
|
|
data/lib/sequel/core.rb
CHANGED
@@ -164,6 +164,21 @@ module Sequel
|
|
164
164
|
JSON::ParserError
|
165
165
|
end
|
166
166
|
|
167
|
+
if RUBY_VERSION >= '3.3'
|
168
|
+
# Create a new module using the block, and set the temporary name
|
169
|
+
# on it using the given a containing module and name.
|
170
|
+
def set_temp_name(mod)
|
171
|
+
mod.set_temporary_name(yield)
|
172
|
+
mod
|
173
|
+
end
|
174
|
+
# :nocov:
|
175
|
+
else
|
176
|
+
def set_temp_name(mod)
|
177
|
+
mod
|
178
|
+
end
|
179
|
+
end
|
180
|
+
# :nocov:
|
181
|
+
|
167
182
|
# Convert given object to json and return the result.
|
168
183
|
# This can be overridden to use an alternative json implementation.
|
169
184
|
def object_to_json(obj, *args, &block)
|
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
# as the dataset class.
|
18
18
|
def dataset_class=(c)
|
19
19
|
unless @dataset_modules.empty?
|
20
|
-
c = Class.new(c)
|
20
|
+
c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"}
|
21
21
|
@dataset_modules.each{|m| c.send(:include, m)}
|
22
22
|
end
|
23
23
|
@dataset_class = c
|
@@ -61,10 +61,10 @@ module Sequel
|
|
61
61
|
# # SELECT id, name FROM table WHERE active ORDER BY id
|
62
62
|
def extend_datasets(mod=nil, &block)
|
63
63
|
raise(Error, "must provide either mod or block, not both") if mod && block
|
64
|
-
mod = Dataset::DatasetModule.new(&block) if block
|
64
|
+
mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"} if block
|
65
65
|
if @dataset_modules.empty?
|
66
66
|
@dataset_modules = [mod]
|
67
|
-
@dataset_class = Class.new(@dataset_class)
|
67
|
+
@dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"}
|
68
68
|
else
|
69
69
|
@dataset_modules << mod
|
70
70
|
end
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -116,6 +116,10 @@ module Sequel
|
|
116
116
|
# :after_connect :: A callable object called after each new connection is made, with the
|
117
117
|
# connection object (and server argument if the callable accepts 2 arguments),
|
118
118
|
# useful for customizations that you want to apply to all connections.
|
119
|
+
# :compare_connections_by_identity :: Whether to use compare_by_identity on hashes that use
|
120
|
+
# connection objects as keys. Defaults to true. This should only
|
121
|
+
# be set to false to work around bugs in libraries or
|
122
|
+
# ruby implementations.
|
119
123
|
# :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
|
120
124
|
# but before any connections are created.
|
121
125
|
# :cache_schema :: Whether schema should be cached for this Database instance
|
@@ -165,7 +169,7 @@ module Sequel
|
|
165
169
|
@schemas = {}
|
166
170
|
@prepared_statements = {}
|
167
171
|
@transactions = {}
|
168
|
-
@transactions.compare_by_identity
|
172
|
+
@transactions.compare_by_identity if typecast_value_boolean(@opts.fetch(:compare_connections_by_identity, true))
|
169
173
|
@symbol_literal_cache = {}
|
170
174
|
|
171
175
|
@timezone = nil
|
@@ -430,6 +430,10 @@ module Sequel
|
|
430
430
|
# add_unique_constraint(:name, name: :unique_name) # ADD CONSTRAINT unique_name UNIQUE (name)
|
431
431
|
#
|
432
432
|
# Supports the same :deferrable option as CreateTableGenerator#column.
|
433
|
+
#
|
434
|
+
# PostgreSQL specific options:
|
435
|
+
#
|
436
|
+
# :using_index :: Use the USING INDEX clause to specify an existing unique index
|
433
437
|
def add_unique_constraint(columns, opts = OPTS)
|
434
438
|
@operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
|
435
439
|
nil
|
@@ -483,6 +487,10 @@ module Sequel
|
|
483
487
|
#
|
484
488
|
# add_primary_key(:id) # ADD COLUMN id serial PRIMARY KEY
|
485
489
|
# add_primary_key([:artist_id, :name]) # ADD PRIMARY KEY (artist_id, name)
|
490
|
+
#
|
491
|
+
# PostgreSQL specific options:
|
492
|
+
#
|
493
|
+
# :using_index :: Use the USING INDEX clause to specify an existing unique index
|
486
494
|
def add_primary_key(name, opts = OPTS)
|
487
495
|
return add_composite_primary_key(name, opts) if name.is_a?(Array)
|
488
496
|
opts = @db.serial_primary_key_options.merge(opts)
|
@@ -19,7 +19,7 @@ module Sequel
|
|
19
19
|
def with_extend(*mods, &block)
|
20
20
|
c = _clone(:freeze=>false)
|
21
21
|
c.extend(*mods) unless mods.empty?
|
22
|
-
c.extend(DatasetModule.new(&block)) if block
|
22
|
+
c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
|
23
23
|
c.freeze
|
24
24
|
end
|
25
25
|
|
@@ -20,7 +20,8 @@ module Sequel
|
|
20
20
|
def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block)
|
21
21
|
code = PREPARED_STATEMENT_MODULE_CODE[code] || code
|
22
22
|
|
23
|
-
Module.new do
|
23
|
+
Module.new do
|
24
|
+
Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"}
|
24
25
|
Array(mods).each do |mod|
|
25
26
|
include mod
|
26
27
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -1238,9 +1238,9 @@ module Sequel
|
|
1238
1238
|
# Note that like Object#extend, when multiple modules are provided
|
1239
1239
|
# as arguments the subclass includes the modules in reverse order.
|
1240
1240
|
def with_extend(*mods, &block)
|
1241
|
-
c = Class.new(self.class)
|
1241
|
+
c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"}
|
1242
1242
|
c.include(*mods) unless mods.empty?
|
1243
|
-
c.include(DatasetModule.new(&block)) if block
|
1243
|
+
c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
|
1244
1244
|
o = c.freeze.allocate
|
1245
1245
|
o.instance_variable_set(:@db, @db)
|
1246
1246
|
o.instance_variable_set(:@opts, @opts)
|
@@ -105,18 +105,23 @@ module Sequel
|
|
105
105
|
1.times do
|
106
106
|
if (conn = super) &&
|
107
107
|
(timer = sync{@connection_timestamps.delete(conn)}) &&
|
108
|
-
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout
|
109
|
-
!db.valid_connection?(conn)
|
108
|
+
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
110
|
+
begin
|
111
|
+
valid = db.valid_connection?(conn)
|
112
|
+
ensure
|
113
|
+
unless valid
|
114
|
+
case pool_type
|
115
|
+
when :sharded_threaded, :sharded_timed_queue
|
116
|
+
sync{@allocated[a.last].delete(Sequel.current)}
|
117
|
+
else
|
118
|
+
sync{@allocated.delete(Sequel.current)}
|
119
|
+
end
|
117
120
|
|
118
|
-
|
119
|
-
|
121
|
+
disconnect_connection(conn)
|
122
|
+
redo if valid == false
|
123
|
+
end
|
124
|
+
end
|
120
125
|
end
|
121
126
|
end
|
122
127
|
|
@@ -27,9 +27,14 @@
|
|
27
27
|
# treating strings as text can break programs, since the type for
|
28
28
|
# literal strings in PostgreSQL is +unknown+, not +text+.
|
29
29
|
#
|
30
|
-
# The conversion is only done for single dimensional arrays that have
|
31
|
-
#
|
32
|
-
# nil values).
|
30
|
+
# The conversion is only done for single dimensional arrays that have two or
|
31
|
+
# more elements, where all elements are of the same class (other than
|
32
|
+
# +nil+ values). You can also do the conversion for arrays of 1 element by setting
|
33
|
+
# <tt>pg_auto_parameterize_min_array_size: 1</tt> Database option. This makes
|
34
|
+
# finding cases that need special handling easier, but it doesn't match
|
35
|
+
# how PostgreSQL internally converts the expression (PostgreSQL converts
|
36
|
+
# <tt>IN (single_value)</tt> to <tt>= single_value</tt>, not
|
37
|
+
# <tt>= ANY(ARRAY[single_value])</tt>).
|
33
38
|
#
|
34
39
|
# Related module: Sequel::Postgres::AutoParameterizeInArray
|
35
40
|
|
@@ -68,7 +73,7 @@ module Sequel
|
|
68
73
|
# The bound variable type string to use for the bound variable array.
|
69
74
|
# Returns nil if a bound variable should not be used for the array.
|
70
75
|
def _bound_variable_type_for_array(r)
|
71
|
-
return unless Array === r && r.size
|
76
|
+
return unless Array === r && r.size >= (db.typecast_value(:integer, db.opts[:pg_auto_parameterize_min_array_size]) || 2)
|
72
77
|
classes = r.map(&:class)
|
73
78
|
classes.uniq!
|
74
79
|
classes.delete(NilClass)
|
@@ -149,12 +149,12 @@ module Sequel
|
|
149
149
|
from(:pg_type).
|
150
150
|
where(:oid=>enum_labels.keys).
|
151
151
|
exclude(:typarray=>0).
|
152
|
-
select_map([:typname, Sequel.cast(:typarray, Integer).as(:v)])
|
152
|
+
select_map([:typname, Sequel.cast(:typarray, Integer).as(:v), Sequel.cast(:oid, Integer).as(:sv)])
|
153
153
|
|
154
154
|
existing_oids = conversion_procs.keys
|
155
|
-
array_types.each do |name, oid|
|
155
|
+
array_types.each do |name, oid, scalar_oid|
|
156
156
|
next if existing_oids.include?(oid)
|
157
|
-
register_array_type(name, :oid=>oid)
|
157
|
+
register_array_type(name, :oid=>oid, :scalar_oid=>scalar_oid)
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
@@ -113,6 +113,7 @@ module Sequel
|
|
113
113
|
# automatically casted to the database type when literalizing.
|
114
114
|
def self.subclass(db_type)
|
115
115
|
Class.new(self) do
|
116
|
+
Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::ArrayRow::_Subclass(#{db_type})"}
|
116
117
|
@db_type = db_type
|
117
118
|
end
|
118
119
|
end
|
@@ -170,6 +171,7 @@ module Sequel
|
|
170
171
|
# type and columns.
|
171
172
|
def self.subclass(db_type, columns)
|
172
173
|
Class.new(self) do
|
174
|
+
Sequel.set_temp_name(self){"Sequel::Postgres::PGRow::HashRow::_Subclass(#{db_type})"}
|
173
175
|
@db_type = db_type
|
174
176
|
@columns = columns
|
175
177
|
end
|
@@ -391,7 +393,7 @@ module Sequel
|
|
391
393
|
db.instance_exec do
|
392
394
|
@row_types = {}
|
393
395
|
@row_schema_types = {}
|
394
|
-
extend(@row_type_method_module = Module.new)
|
396
|
+
extend(@row_type_method_module = Sequel.set_temp_name(Module.new){"Sequel::Postgres::PGRow::DatabaseMethods::_RowTypeMethodModule"})
|
395
397
|
add_conversion_proc(2249, PGRow::Parser.new(:converter=>PGRow::ArrayRow))
|
396
398
|
if respond_to?(:register_array_type)
|
397
399
|
register_array_type('record', :oid=>2287, :scalar_oid=>2249)
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The query_blocker extension adds Database#block_queries.
|
4
|
+
# Inside the block passed to #block_queries, any attempts to
|
5
|
+
# execute a query/statement on the database will raise a
|
6
|
+
# Sequel::QueryBlocker::BlockedQuery exception.
|
7
|
+
#
|
8
|
+
# DB.extension :query_blocker
|
9
|
+
# DB.block_queries do
|
10
|
+
# ds = DB[:table] # No exception
|
11
|
+
# ds = ds.where(column: 1) # No exception
|
12
|
+
# ds.all # Exception raised
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# To handle concurrency, you can pass a :scope option:
|
16
|
+
#
|
17
|
+
# # Current Thread
|
18
|
+
# DB.block_queries(scope: :thread){}
|
19
|
+
#
|
20
|
+
# # Current Fiber
|
21
|
+
# DB.block_queries(scope: :fiber){}
|
22
|
+
#
|
23
|
+
# # Specific Thread
|
24
|
+
# DB.block_queries(scope: Thread.current){}
|
25
|
+
#
|
26
|
+
# # Specific Fiber
|
27
|
+
# DB.block_queries(scope: Fiber.current){}
|
28
|
+
#
|
29
|
+
# Database#block_queries is useful for blocking queries inside
|
30
|
+
# the block. However, there may be cases where you want to
|
31
|
+
# allow queries in specific places inside a block_queries block.
|
32
|
+
# You can use Database#allow_queries for that:
|
33
|
+
#
|
34
|
+
# DB.block_queries do
|
35
|
+
# DB.allow_queries do
|
36
|
+
# DB[:table].all # Query allowed
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# DB[:table].all # Exception raised
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# When mixing block_queries and allow_queries with scopes, the
|
43
|
+
# narrowest scope has priority. So if you are blocking with
|
44
|
+
# :thread scope, and allowing with :fiber scope, queries in the
|
45
|
+
# current fiber will be allowed, but queries in different fibers of
|
46
|
+
# the current thread will be blocked.
|
47
|
+
#
|
48
|
+
# Note that this should catch all queries executed through the
|
49
|
+
# Database instance. Whether it catches queries executed directly
|
50
|
+
# on a connection object depends on the adapter in use.
|
51
|
+
#
|
52
|
+
# Related module: Sequel::QueryBlocker
|
53
|
+
|
54
|
+
require "fiber"
|
55
|
+
|
56
|
+
#
|
57
|
+
module Sequel
|
58
|
+
module QueryBlocker
|
59
|
+
# Exception class raised if there is an attempt to execute a
|
60
|
+
# query/statement on the database inside a block passed to
|
61
|
+
# block_queries.
|
62
|
+
class BlockedQuery < Sequel::Error
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.extended(db)
|
66
|
+
db.instance_exec do
|
67
|
+
@blocked_query_scopes ||= {}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# If checking a connection for validity, and a BlockedQuery exception is
|
72
|
+
# raised, treat it as a valid connection. You cannot check whether the
|
73
|
+
# connection is valid without issuing a query, and if queries are blocked,
|
74
|
+
# you need to assume it is valid or assume it is not. Since it most cases
|
75
|
+
# it will be valid, this assumes validity.
|
76
|
+
def valid_connection?(conn)
|
77
|
+
super
|
78
|
+
rescue BlockedQuery
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
# Check whether queries are blocked before executing them.
|
83
|
+
def log_connection_yield(sql, conn, args=nil)
|
84
|
+
# All database adapters should be calling this method around
|
85
|
+
# query execution (otherwise the queries would not get logged),
|
86
|
+
# ensuring the blocking is checked. Any database adapter issuing
|
87
|
+
# a query without calling this method is considered buggy.
|
88
|
+
check_blocked_queries!
|
89
|
+
super
|
90
|
+
end
|
91
|
+
|
92
|
+
# Whether queries are currently blocked.
|
93
|
+
def block_queries?
|
94
|
+
b = @blocked_query_scopes
|
95
|
+
b.fetch(Fiber.current) do
|
96
|
+
b.fetch(Thread.current) do
|
97
|
+
b.fetch(:global, false)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Allow queries inside the block. Only useful if they are already blocked
|
103
|
+
# for the same scope. Useful for blocking queries generally, and only allowing
|
104
|
+
# them in specific places. Takes the same :scope option as #block_queries.
|
105
|
+
def allow_queries(opts=OPTS, &block)
|
106
|
+
_allow_or_block_queries(false, opts, &block)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Reject (raise an BlockedQuery exception) if there is an attempt to execute
|
110
|
+
# a query/statement inside the block.
|
111
|
+
#
|
112
|
+
# The :scope option indicates which queries are rejected inside the block:
|
113
|
+
#
|
114
|
+
# :global :: This is the default, and rejects all queries.
|
115
|
+
# :thread :: Reject all queries in the current thread.
|
116
|
+
# :fiber :: Reject all queries in the current fiber.
|
117
|
+
# Thread :: Reject all queries in the given thread.
|
118
|
+
# Fiber :: Reject all queries in the given fiber.
|
119
|
+
def block_queries(opts=OPTS, &block)
|
120
|
+
_allow_or_block_queries(true, opts, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Internals of block_queries and allow_queries.
|
126
|
+
def _allow_or_block_queries(value, opts)
|
127
|
+
scope = query_blocker_scope(opts)
|
128
|
+
prev_value = nil
|
129
|
+
scopes = @blocked_query_scopes
|
130
|
+
|
131
|
+
begin
|
132
|
+
Sequel.synchronize do
|
133
|
+
prev_value = scopes[scope]
|
134
|
+
scopes[scope] = value
|
135
|
+
end
|
136
|
+
|
137
|
+
yield
|
138
|
+
ensure
|
139
|
+
Sequel.synchronize do
|
140
|
+
if prev_value.nil?
|
141
|
+
scopes.delete(scope)
|
142
|
+
else
|
143
|
+
scopes[scope] = prev_value
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# The scope for the query block, either :global, or a Thread or Fiber instance.
|
150
|
+
def query_blocker_scope(opts)
|
151
|
+
case scope = opts[:scope]
|
152
|
+
when nil
|
153
|
+
:global
|
154
|
+
when :global, Thread, Fiber
|
155
|
+
scope
|
156
|
+
when :thread
|
157
|
+
Thread.current
|
158
|
+
when :fiber
|
159
|
+
Fiber.current
|
160
|
+
else
|
161
|
+
raise Sequel::Error, "invalid scope given to block_queries: #{scope.inspect}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Raise a BlockQuery exception if queries are currently blocked.
|
166
|
+
def check_blocked_queries!
|
167
|
+
raise BlockedQuery, "cannot execute query inside a block_queries block" if block_queries?
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
Database.register_extension(:query_blocker, QueryBlocker)
|
172
|
+
end
|
@@ -14,6 +14,7 @@ module Sequel
|
|
14
14
|
module SQL
|
15
15
|
class VirtualRow < BasicObject
|
16
16
|
include(Module.new do
|
17
|
+
Sequel.set_temp_name(self){"Sequel::SQL:VirtualRow::_MethodBlockMethodMissing"}
|
17
18
|
# Handle blocks passed to methods and change the behavior.
|
18
19
|
def method_missing(m, *args, &block)
|
19
20
|
if block
|
@@ -693,6 +693,9 @@ module Sequel
|
|
693
693
|
|
694
694
|
# The predicate condition to use for the eager_loader.
|
695
695
|
def eager_loading_predicate_condition(keys)
|
696
|
+
if transform = self[:eager_loading_predicate_transform]
|
697
|
+
keys = transform.call(keys, self)
|
698
|
+
end
|
696
699
|
{predicate_key=>keys}
|
697
700
|
end
|
698
701
|
|
@@ -759,7 +762,15 @@ module Sequel
|
|
759
762
|
def placeholder_eager_loader
|
760
763
|
cached_fetch(:placeholder_eager_loader) do
|
761
764
|
eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
|
762
|
-
|
765
|
+
arg = pl.arg
|
766
|
+
|
767
|
+
if transform = self[:eager_loading_predicate_transform]
|
768
|
+
arg = arg.transform do |v|
|
769
|
+
transform.call(v, self)
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
|
763
774
|
end
|
764
775
|
end
|
765
776
|
end
|
@@ -1707,6 +1718,9 @@ module Sequel
|
|
1707
1718
|
# record should be populated.
|
1708
1719
|
# :eager_loader_key :: A symbol for the key column to use to populate the key_hash
|
1709
1720
|
# for the eager loader. Can be set to nil to not populate the key_hash.
|
1721
|
+
# :eager_loading_predicate_transform :: A callable object with which to transform the predicate key values used
|
1722
|
+
# when eager loading. Called with two arguments, the array of predicate key
|
1723
|
+
# values, and a the reflection for the association being eager loaded.
|
1710
1724
|
# :extend :: A module or array of modules to extend the dataset with.
|
1711
1725
|
# :filter_limit_strategy :: Determines the strategy used for enforcing limits and offsets when filtering by
|
1712
1726
|
# limited associations. Possible options are :window_function, :distinct_on, or
|
@@ -1769,6 +1783,9 @@ module Sequel
|
|
1769
1783
|
# Set to nil to not define a setter method for the association.
|
1770
1784
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1771
1785
|
# loading limited associations using the default :union strategy.
|
1786
|
+
# :use_placeholder_loader :: Whether to use a placeholder loader when eager loading the
|
1787
|
+
# association. Can be set to false to disable the use of a placeholder
|
1788
|
+
# loader if one would be used by default.
|
1772
1789
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
1773
1790
|
# === :many_to_one
|
1774
1791
|
# :key :: foreign key in current model's table that references
|
@@ -1891,7 +1908,7 @@ module Sequel
|
|
1891
1908
|
raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
|
1892
1909
|
end
|
1893
1910
|
|
1894
|
-
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1911
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph] unless opts.include?(:use_placeholder_loader)
|
1895
1912
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1896
1913
|
opts[:graph_join_type] ||= :left_outer
|
1897
1914
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -184,7 +184,7 @@ module Sequel
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
klass = Class.new(self)
|
187
|
+
klass = Sequel.set_temp_name(Class.new(self)){"Sequel::_Model(#{source.inspect})"}
|
188
188
|
|
189
189
|
if source.is_a?(::Sequel::Database)
|
190
190
|
klass.db = source
|
@@ -768,7 +768,8 @@ module Sequel
|
|
768
768
|
# default behavior.
|
769
769
|
def dataset_methods_module
|
770
770
|
return @dataset_methods_module if defined?(@dataset_methods_module)
|
771
|
-
|
771
|
+
mod_name = "#{name}::@dataset_methods_module"
|
772
|
+
Sequel.synchronize{@dataset_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
772
773
|
extend(@dataset_methods_module)
|
773
774
|
@dataset_methods_module
|
774
775
|
end
|
@@ -956,7 +957,10 @@ END
|
|
956
957
|
# Module that the class includes that holds methods the class adds for column accessors and
|
957
958
|
# associations so that the methods can be overridden with +super+.
|
958
959
|
def overridable_methods_module
|
959
|
-
|
960
|
+
return @overridable_methods_module if defined?(@overridable_methods_module)
|
961
|
+
mod_name = "#{name}::@overridable_methods_module"
|
962
|
+
Sequel.synchronize{@overridable_methods_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
963
|
+
include(@overridable_methods_module)
|
960
964
|
@overridable_methods_module
|
961
965
|
end
|
962
966
|
|
@@ -1610,7 +1614,7 @@ END
|
|
1610
1614
|
@skip_validation_on_next_save = true
|
1611
1615
|
end
|
1612
1616
|
|
1613
|
-
# Returns
|
1617
|
+
# Returns naked dataset that should return only the row related to this instance.
|
1614
1618
|
#
|
1615
1619
|
# Artist[1].this
|
1616
1620
|
# # SELECT * FROM artists WHERE (id = 1) LIMIT 1
|
data/lib/sequel/plugins/enum.rb
CHANGED
@@ -32,6 +32,7 @@ module Sequel
|
|
32
32
|
def self.apply(model, &block)
|
33
33
|
model.instance_exec do
|
34
34
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
35
|
+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(InvertedSubsets)"}
|
35
36
|
include DatasetModuleMethods
|
36
37
|
if block
|
37
38
|
define_method(:inverted_subset_name, &block)
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
# :dataset :: The base dataset to use for the lazy attribute lookup
|
65
65
|
# :table :: The table name to use to qualify the attribute and primary key columns.
|
66
66
|
def define_lazy_attribute_getter(a, opts=OPTS)
|
67
|
-
include(@lazy_attributes_module ||= Module.new) unless @lazy_attributes_module
|
67
|
+
include(@lazy_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@lazy_attributes_module"}) unless @lazy_attributes_module
|
68
68
|
@lazy_attributes_module.class_eval do
|
69
69
|
define_method(a) do
|
70
70
|
if !values.has_key?(a) && !new?
|
@@ -129,7 +129,7 @@ module Sequel
|
|
129
129
|
#
|
130
130
|
# If a block is provided, it is used to set the :reject_if option.
|
131
131
|
def nested_attributes(*associations, &block)
|
132
|
-
include(@nested_attributes_module ||= Module.new) unless @nested_attributes_module
|
132
|
+
include(@nested_attributes_module ||= Sequel.set_temp_name(Module.new){"#{name}::@nested_attributes_module"}) unless @nested_attributes_module
|
133
133
|
opts = associations.last.is_a?(Hash) ? associations.pop : OPTS
|
134
134
|
reflections = associations.map{|a| association_reflection(a) || raise(Error, "no association named #{a} for #{self}")}
|
135
135
|
reflections.each do |r|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The pg_eager_any_typed_array plugin automatically converts
|
6
|
+
# the predicate expressions used for eager loading from:
|
7
|
+
#
|
8
|
+
# table.column IN (value_list)
|
9
|
+
#
|
10
|
+
# to:
|
11
|
+
#
|
12
|
+
# table.column = ANY(array_expr::type[])
|
13
|
+
#
|
14
|
+
# This makes it easier to use the pg_auto_parameterize_in_array
|
15
|
+
# extension with the :treat_string_list_as_text_array option,
|
16
|
+
# when using foreign keys with non-text database types that are represented
|
17
|
+
# by Ruby strings, such as enum and uuid types.
|
18
|
+
#
|
19
|
+
# Most association types that ship with Sequel have their predicate
|
20
|
+
# expressions converted by this plugin. Here are the exceptions:
|
21
|
+
#
|
22
|
+
# * associations using composite predicate keys
|
23
|
+
# * many_to_pg_array associations
|
24
|
+
# * many_to_many/one_through_one associations using :join_table_db option
|
25
|
+
# * many_through_many/one_through_many associations using
|
26
|
+
# :separate_table_per_query option
|
27
|
+
#
|
28
|
+
# To avoid predicate conversion for particular associations, set the
|
29
|
+
# :eager_loading_predicate_transform association option to nil/false.
|
30
|
+
#
|
31
|
+
# This plugin loads the pg_array extension into the model's Database.
|
32
|
+
module PgEagerAnyTypedArray
|
33
|
+
# Add the pg_array extension to the database
|
34
|
+
def self.apply(model)
|
35
|
+
model.db.extension(:pg_array)
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
TRANSFORM = proc do |values, ref|
|
40
|
+
type = ref.send(:cached_fetch, :_pg_eager_any_typed_array_type) do
|
41
|
+
key = ref.predicate_key
|
42
|
+
next if key.is_a?(Array)
|
43
|
+
|
44
|
+
while key.is_a?(SQL::QualifiedIdentifier)
|
45
|
+
key = key.column
|
46
|
+
end
|
47
|
+
|
48
|
+
# :nocov:
|
49
|
+
# many_to_pg_array association type does not need changes, as it
|
50
|
+
# already converts the values to a typed postgres array, it does
|
51
|
+
# not call the code that uses :eager_loading_predicate_transform.
|
52
|
+
#
|
53
|
+
# No association type that ships with Sequel can reach this code
|
54
|
+
# unless it is one of these types, but external association types
|
55
|
+
# could potentially reach it.
|
56
|
+
sch = case ref[:type]
|
57
|
+
# :nocov:
|
58
|
+
when :many_to_one, :one_to_one, :one_to_many, :pg_array_to_many
|
59
|
+
ref.associated_class.db_schema
|
60
|
+
when :many_to_many, :one_through_one
|
61
|
+
# Not compatible with the :join_table_db option, but that option
|
62
|
+
# does not call into this code.
|
63
|
+
Hash[ref.associated_class.db.schema(ref.join_table_source)]
|
64
|
+
when :many_through_many, :one_through_many
|
65
|
+
# Not compatible with the :separate_query_per_table option, but
|
66
|
+
# that option does not call into this code.
|
67
|
+
Hash[ref.associated_class.db.schema(ref[:through][0][:table])]
|
68
|
+
end
|
69
|
+
|
70
|
+
if sch && (sch = sch[key])
|
71
|
+
sch[:db_type]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if type
|
76
|
+
Sequel.function(:ANY, Sequel.pg_array(values, type))
|
77
|
+
else
|
78
|
+
values
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Set the :eager_loading_predicate_transform option if not already set
|
83
|
+
def associate(type, name, opts = OPTS, &block)
|
84
|
+
res = super
|
85
|
+
|
86
|
+
unless res.has_key?(:eager_loading_predicate_transform)
|
87
|
+
res[:eager_loading_predicate_transform] = TRANSFORM
|
88
|
+
end
|
89
|
+
|
90
|
+
res
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -81,7 +81,7 @@ module Sequel
|
|
81
81
|
|
82
82
|
opts = opts.dup
|
83
83
|
opts[:class] = model
|
84
|
-
opts[:methods_module] = Module.new
|
84
|
+
opts[:methods_module] = Sequel.set_temp_name(Module.new){"#{model.name}::_rcte_tree[:methods_module]"}
|
85
85
|
opts[:union_all] = opts[:union_all].nil? ? true : opts[:union_all]
|
86
86
|
model.send(:include, opts[:methods_module])
|
87
87
|
|
@@ -146,7 +146,7 @@ module Sequel
|
|
146
146
|
# Add serializated attribute acessor methods to the serialization_module
|
147
147
|
def define_serialized_attribute_accessor(serializer, deserializer, *columns)
|
148
148
|
m = self
|
149
|
-
include(@serialization_module ||= Module.new) unless @serialization_module
|
149
|
+
include(@serialization_module ||= Sequel.set_temp_name(Module.new){"#{name}::@serialization_module"}) unless @serialization_module
|
150
150
|
@serialization_module.class_eval do
|
151
151
|
columns.each do |column|
|
152
152
|
m.serialization_map[column] = serializer
|
@@ -40,7 +40,8 @@ module Sequel
|
|
40
40
|
# Album.sql_comments_dataset_methods :to_csv # csv_serializer plugin
|
41
41
|
#
|
42
42
|
# In order for the sql_comments plugin to work, the sql_comments
|
43
|
-
# Database extension must be loaded into the model's database
|
43
|
+
# Database extension must be loaded into the model's database, so
|
44
|
+
# loading the plugin does this automatically.
|
44
45
|
#
|
45
46
|
# Note that in order to make sure SQL comments are included, some
|
46
47
|
# optimizations are disabled if this plugin is loaded.
|
@@ -67,6 +68,10 @@ module Sequel
|
|
67
68
|
# :nocov:
|
68
69
|
end
|
69
70
|
|
71
|
+
def self.apply(model)
|
72
|
+
model.db.extension(:sql_comments)
|
73
|
+
end
|
74
|
+
|
70
75
|
def self.configure(model)
|
71
76
|
model.send(:reset_fast_pk_lookup_sql)
|
72
77
|
end
|
@@ -85,7 +90,7 @@ module Sequel
|
|
85
90
|
# Use automatic SQL comments for the given dataset methods.
|
86
91
|
def sql_comments_dataset_methods(*meths)
|
87
92
|
unless @_sql_comments_dataset_module
|
88
|
-
dataset_module(@_sql_comments_dataset_module = Module.new)
|
93
|
+
dataset_module(@_sql_comments_dataset_module = Sequel.set_temp_name(Module.new){"#{name}::@_sql_comments_dataset_module"})
|
89
94
|
end
|
90
95
|
_sql_comments_methods(@_sql_comments_dataset_module, :dataset, meths)
|
91
96
|
end
|
@@ -48,6 +48,7 @@ module Sequel
|
|
48
48
|
def self.apply(model, &block)
|
49
49
|
model.instance_exec do
|
50
50
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
51
|
+
Sequel.set_temp_name(self){"#{model.name}::@dataset_module_class(SubsetConditions)"}
|
51
52
|
include DatasetModuleMethods
|
52
53
|
end
|
53
54
|
end
|
@@ -99,7 +99,8 @@ module Sequel
|
|
99
99
|
# it before calling creating this module.
|
100
100
|
dataset_methods_module
|
101
101
|
|
102
|
-
|
102
|
+
mod_name = "#{name}::@subset_static_cache_module"
|
103
|
+
Sequel.synchronize{@subset_static_cache_module ||= Sequel.set_temp_name(Module.new){mod_name}}
|
103
104
|
extend(@subset_static_cache_module)
|
104
105
|
@subset_static_cache_module
|
105
106
|
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -1127,7 +1127,13 @@ module Sequel
|
|
1127
1127
|
when DelayedEvaluation
|
1128
1128
|
Sequel.delay{|ds| from_value_pair(l, r.call(ds))}
|
1129
1129
|
when Dataset::PlaceholderLiteralizer::Argument
|
1130
|
-
r.
|
1130
|
+
prev_transform = r.instance_variable_get(:@transformer)
|
1131
|
+
r.transform do |v|
|
1132
|
+
if prev_transform
|
1133
|
+
v = prev_transform.call(v)
|
1134
|
+
end
|
1135
|
+
from_value_pair(l, v)
|
1136
|
+
end
|
1131
1137
|
else
|
1132
1138
|
new(:'=', l, r)
|
1133
1139
|
end
|
@@ -1915,6 +1921,7 @@ module Sequel
|
|
1915
1921
|
end
|
1916
1922
|
|
1917
1923
|
m = Module.new do
|
1924
|
+
Sequel.set_temp_name(Module.new){"Sequel::SQL::VirtualRow::_BaseMethodMissing"}
|
1918
1925
|
# Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending
|
1919
1926
|
# on arguments and whether a block is provided. Does not currently call the block.
|
1920
1927
|
# See the class level documentation.
|
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 = 90
|
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,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.90.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bigdecimal
|
@@ -273,6 +273,7 @@ files:
|
|
273
273
|
- lib/sequel/extensions/pretty_table.rb
|
274
274
|
- lib/sequel/extensions/provenance.rb
|
275
275
|
- lib/sequel/extensions/query.rb
|
276
|
+
- lib/sequel/extensions/query_blocker.rb
|
276
277
|
- lib/sequel/extensions/round_timestamps.rb
|
277
278
|
- lib/sequel/extensions/run_transaction_hooks.rb
|
278
279
|
- lib/sequel/extensions/s.rb
|
@@ -370,6 +371,7 @@ files:
|
|
370
371
|
- lib/sequel/plugins/paged_operations.rb
|
371
372
|
- lib/sequel/plugins/pg_array_associations.rb
|
372
373
|
- lib/sequel/plugins/pg_auto_constraint_validations.rb
|
374
|
+
- lib/sequel/plugins/pg_eager_any_typed_array.rb
|
373
375
|
- lib/sequel/plugins/pg_row.rb
|
374
376
|
- lib/sequel/plugins/pg_xmin_optimistic_locking.rb
|
375
377
|
- lib/sequel/plugins/prepared_statements.rb
|