sequel 5.89.0 → 5.91.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/ado.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +2 -2
- data/lib/sequel/adapters/shared/mssql.rb +3 -3
- data/lib/sequel/adapters/shared/mysql.rb +3 -3
- data/lib/sequel/adapters/shared/postgres.rb +2 -2
- data/lib/sequel/database/query.rb +11 -11
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model/associations.rb +28 -3
- data/lib/sequel/model/base.rb +7 -3
- data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
- data/lib/sequel/plugins/nested_attributes.rb +9 -4
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/sql_comments.rb +6 -1
- data/lib/sequel/plugins/subset_static_cache.rb +2 -1
- data/lib/sequel/sql.rb +7 -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: d13e0998a9eb78392a4f9f7793b2ceceebf87fa5a4dd6f9fcfd5bbd05a686d83
|
4
|
+
data.tar.gz: d4c7da82fb811928c58a096b2d5fc500cdb7cee1bbff14795817b5bd3f4d3360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c2bbfc37fbd2b97796c96352a0f1d7d41053f91f4b61e3888a5aea712b9a59c7600e8ed58a15919cef7d8b0cc1c35e4bc3d142206ac245ed6f4f13575188f3b9
|
7
|
+
data.tar.gz: 8f4c0b7b95ec967c471455df69e8b5b5ff9c2104790c7c18abaa9601f2e7c4514952d4410aac1e3f720304f30af2d7299ea0f1233a7e083bcb901c8a03f697bb
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -179,7 +179,7 @@ module Sequel
|
|
179
179
|
|
180
180
|
def adapter_initialize
|
181
181
|
case @opts[:conn_string]
|
182
|
-
when /Microsoft\.(Jet|ACE)\.OLEDB/
|
182
|
+
when /Microsoft\.(Jet|ACE)\.OLEDB/i
|
183
183
|
require_relative 'ado/access'
|
184
184
|
extend Sequel::ADO::Access::DatabaseMethods
|
185
185
|
self.dataset_class = ADO::Access::Dataset
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -636,11 +636,11 @@ module Sequel
|
|
636
636
|
def schema_column_set_db_type(schema)
|
637
637
|
case schema[:type]
|
638
638
|
when :string
|
639
|
-
if schema[:db_type] =~ /\A(character( varying)?|n?(var)?char2?)\z/
|
639
|
+
if schema[:db_type] =~ /\A(character( varying)?|n?(var)?char2?)\z/i && schema[:column_size] > 0
|
640
640
|
schema[:db_type] += "(#{schema[:column_size]})"
|
641
641
|
end
|
642
642
|
when :decimal
|
643
|
-
if schema[:db_type] =~ /\A(decimal|numeric)\z/
|
643
|
+
if schema[:db_type] =~ /\A(decimal|numeric)\z/i && schema[:column_size] > 0 && schema[:scale] >= 0
|
644
644
|
schema[:db_type] += "(#{schema[:column_size]}, #{schema[:scale]})"
|
645
645
|
end
|
646
646
|
end
|
@@ -480,11 +480,11 @@ module Sequel
|
|
480
480
|
|
481
481
|
def schema_column_type(db_type)
|
482
482
|
case db_type
|
483
|
-
when /\A(?:bit)\z/
|
483
|
+
when /\A(?:bit)\z/i
|
484
484
|
:boolean
|
485
|
-
when /\A(?:(?:small)?money)\z/
|
485
|
+
when /\A(?:(?:small)?money)\z/i
|
486
486
|
:decimal
|
487
|
-
when /\A(timestamp|rowversion)\z/
|
487
|
+
when /\A(timestamp|rowversion)\z/i
|
488
488
|
:blob
|
489
489
|
else
|
490
490
|
super
|
@@ -550,11 +550,11 @@ module Sequel
|
|
550
550
|
|
551
551
|
def schema_column_type(db_type)
|
552
552
|
case db_type
|
553
|
-
when /\Aset/
|
553
|
+
when /\Aset/i
|
554
554
|
:set
|
555
|
-
when /\Amediumint/
|
555
|
+
when /\Amediumint/i
|
556
556
|
:integer
|
557
|
-
when /\Amediumtext/
|
557
|
+
when /\Amediumtext/i
|
558
558
|
:string
|
559
559
|
else
|
560
560
|
super
|
@@ -1663,9 +1663,9 @@ module Sequel
|
|
1663
1663
|
# Handle interval and citext types.
|
1664
1664
|
def schema_column_type(db_type)
|
1665
1665
|
case db_type
|
1666
|
-
when /\Ainterval\z/
|
1666
|
+
when /\Ainterval\z/i
|
1667
1667
|
:interval
|
1668
|
-
when /\Acitext\z/
|
1668
|
+
when /\Acitext\z/i
|
1669
1669
|
:string
|
1670
1670
|
else
|
1671
1671
|
super
|
@@ -170,7 +170,7 @@ module Sequel
|
|
170
170
|
c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type]) unless c.has_key?(:ruby_default)
|
171
171
|
if c[:primary_key] && !auto_increment_set
|
172
172
|
# If adapter didn't set it, assume that integer primary keys are auto incrementing
|
173
|
-
c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/
|
173
|
+
c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/i)
|
174
174
|
end
|
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
|
@@ -390,25 +390,25 @@ module Sequel
|
|
390
390
|
# such as :integer or :string.
|
391
391
|
def schema_column_type(db_type)
|
392
392
|
case db_type
|
393
|
-
when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/
|
393
|
+
when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/i
|
394
394
|
:string
|
395
|
-
when /\A(int(eger)?|(big|small|tiny)int)/
|
395
|
+
when /\A(int(eger)?|(big|small|tiny)int)/i
|
396
396
|
:integer
|
397
|
-
when /\Adate\z/
|
397
|
+
when /\Adate\z/i
|
398
398
|
:date
|
399
|
-
when /\A((small)?datetime
|
399
|
+
when /\A((small)?datetime(\(\d\))?|timestamp(\(\d\))?( with(out)? time zone)?)\z/i
|
400
400
|
:datetime
|
401
|
-
when /\Atime( with(out)? time zone)?\z/
|
401
|
+
when /\Atime( with(out)? time zone)?\z/i
|
402
402
|
:time
|
403
|
-
when /\A(bool(ean)?)\z/
|
403
|
+
when /\A(bool(ean)?)\z/i
|
404
404
|
:boolean
|
405
|
-
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/
|
405
|
+
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/i
|
406
406
|
:float
|
407
|
-
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/
|
407
|
+
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/i
|
408
408
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
409
|
-
when /bytea|blob|image|(var)?binary/
|
409
|
+
when /bytea|blob|image|(var)?binary/i
|
410
410
|
:blob
|
411
|
-
when /\Aenum/
|
411
|
+
when /\Aenum/i
|
412
412
|
:enum
|
413
413
|
end
|
414
414
|
end
|
@@ -21,15 +21,26 @@
|
|
21
21
|
# DateTime :: timestamp (or timestamptz if pg_timestamptz extension is used)
|
22
22
|
# Sequel::SQLTime :: time
|
23
23
|
# Sequel::SQL::Blob :: bytea
|
24
|
+
#
|
25
|
+
# Arrays of string values are not automatically converted by default, because the Ruby
|
26
|
+
# String class can represent a number of different database types. To convert
|
27
|
+
# arrays of Ruby strings to an untyped array (a query parameter with no explicit
|
28
|
+
# type cast), set the +:treat_string_list_as_untyped_array+ Database option
|
29
|
+
# before loading the extension.
|
24
30
|
#
|
25
|
-
#
|
26
|
-
# +:treat_string_list_as_text_array+ Database option is used. This
|
27
|
-
#
|
28
|
-
#
|
31
|
+
# If you will only be using arrays of Ruby strings that represent the +text+ type,
|
32
|
+
# you can use the +:treat_string_list_as_text_array+ Database option is used. This
|
33
|
+
# can break programs, since the type for literal strings in PostgreSQL is +unknown+,
|
34
|
+
# not +text+.
|
29
35
|
#
|
30
|
-
# The conversion is only done for single dimensional arrays that have
|
31
|
-
#
|
32
|
-
# nil values).
|
36
|
+
# The conversion is only done for single dimensional arrays that have two or
|
37
|
+
# more elements, where all elements are of the same class (other than
|
38
|
+
# +nil+ values). You can also do the conversion for arrays of 1 element by setting
|
39
|
+
# <tt>pg_auto_parameterize_min_array_size: 1</tt> Database option. This makes
|
40
|
+
# finding cases that need special handling easier, but it doesn't match
|
41
|
+
# how PostgreSQL internally converts the expression (PostgreSQL converts
|
42
|
+
# <tt>IN (single_value)</tt> to <tt>= single_value</tt>, not
|
43
|
+
# <tt>= ANY(ARRAY[single_value])</tt>).
|
33
44
|
#
|
34
45
|
# Related module: Sequel::Postgres::AutoParameterizeInArray
|
35
46
|
|
@@ -37,6 +48,47 @@ module Sequel
|
|
37
48
|
module Postgres
|
38
49
|
# Enable automatically parameterizing queries.
|
39
50
|
module AutoParameterizeInArray
|
51
|
+
module TreatStringListAsUntypedArray
|
52
|
+
# Sentinal value to use as an auto param type to use auto parameterization
|
53
|
+
# of a string array without an explicit type cast.
|
54
|
+
NO_EXPLICIT_CAST = Object.new.freeze
|
55
|
+
|
56
|
+
# Wrapper for untyped PGArray values that will be parameterized directly
|
57
|
+
# into the query. This should only be used in cases where you know the
|
58
|
+
# value should be added as a query parameter.
|
59
|
+
class ParameterizedUntypedPGArray < SQL::Wrapper
|
60
|
+
def to_s_append(ds, sql)
|
61
|
+
sql.add_arg(@value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Recognize NO_EXPLICIT_CAST sentinal value and use wrapped
|
68
|
+
# PGArray that will be parameterized into the query.
|
69
|
+
def _convert_array_to_pg_array_with_type(r, type)
|
70
|
+
if NO_EXPLICIT_CAST.equal?(type)
|
71
|
+
ParameterizedUntypedPGArray.new(Sequel.pg_array(r))
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Use a query parameter with no type cast for string arrays.
|
78
|
+
def _bound_variable_type_for_string_array(r)
|
79
|
+
NO_EXPLICIT_CAST
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module TreatStringListAsTextArray
|
84
|
+
private
|
85
|
+
|
86
|
+
# Assume all string arrays used on RHS of IN/NOT IN are for type text[]
|
87
|
+
def _bound_variable_type_for_string_array(r)
|
88
|
+
"text"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
40
92
|
# Transform column IN (...) expressions into column = ANY($)
|
41
93
|
# and column NOT IN (...) expressions into column != ALL($)
|
42
94
|
# using an array bound variable for the ANY/ALL argument,
|
@@ -56,7 +108,7 @@ module Sequel
|
|
56
108
|
op = :!=
|
57
109
|
func = :ALL
|
58
110
|
end
|
59
|
-
args = [l, Sequel.function(func,
|
111
|
+
args = [l, Sequel.function(func, _convert_array_to_pg_array_with_type(r, type))]
|
60
112
|
end
|
61
113
|
end
|
62
114
|
|
@@ -68,7 +120,7 @@ module Sequel
|
|
68
120
|
# The bound variable type string to use for the bound variable array.
|
69
121
|
# Returns nil if a bound variable should not be used for the array.
|
70
122
|
def _bound_variable_type_for_array(r)
|
71
|
-
return unless Array === r && r.size
|
123
|
+
return unless Array === r && r.size >= pg_auto_parameterize_min_array_size
|
72
124
|
classes = r.map(&:class)
|
73
125
|
classes.uniq!
|
74
126
|
classes.delete(NilClass)
|
@@ -81,7 +133,7 @@ module Sequel
|
|
81
133
|
# arrays natively (though the SQL used is different)
|
82
134
|
"int8"
|
83
135
|
elsif klass == String
|
84
|
-
|
136
|
+
_bound_variable_type_for_string_array(r)
|
85
137
|
elsif klass == BigDecimal
|
86
138
|
"numeric"
|
87
139
|
elsif klass == Date
|
@@ -100,11 +152,42 @@ module Sequel
|
|
100
152
|
"bytea"
|
101
153
|
end
|
102
154
|
end
|
155
|
+
|
156
|
+
# Do not auto parameterize string arrays by default.
|
157
|
+
def _bound_variable_type_for_string_array(r)
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
|
161
|
+
# The minimium size of array to auto parameterize.
|
162
|
+
def pg_auto_parameterize_min_array_size
|
163
|
+
2
|
164
|
+
end
|
165
|
+
|
166
|
+
# Convert RHS of IN/NOT IN operator to PGArray with given type.
|
167
|
+
def _convert_array_to_pg_array_with_type(r, type)
|
168
|
+
Sequel.pg_array(r, type)
|
169
|
+
end
|
103
170
|
end
|
104
171
|
end
|
105
172
|
|
106
173
|
Database.register_extension(:pg_auto_parameterize_in_array) do |db|
|
107
174
|
db.extension(:pg_array, :pg_auto_parameterize)
|
108
175
|
db.extend_datasets(Postgres::AutoParameterizeInArray)
|
176
|
+
|
177
|
+
if db.typecast_value(:boolean, db.opts[:treat_string_list_as_text_array])
|
178
|
+
db.extend_datasets(Postgres::AutoParameterizeInArray::TreatStringListAsTextArray)
|
179
|
+
elsif db.typecast_value(:boolean, db.opts[:treat_string_list_as_untyped_array])
|
180
|
+
db.extend_datasets(Postgres::AutoParameterizeInArray::TreatStringListAsUntypedArray)
|
181
|
+
end
|
182
|
+
|
183
|
+
if min_array_size = db.opts[:pg_auto_parameterize_min_array_size]
|
184
|
+
min_array_size = db.typecast_value(:integer, min_array_size)
|
185
|
+
mod = Module.new do
|
186
|
+
define_method(:pg_auto_parameterize_min_array_size){min_array_size}
|
187
|
+
private :pg_auto_parameterize_min_array_size
|
188
|
+
end
|
189
|
+
Sequel.set_temp_name(mod){"Sequel::Postgres::AutoParameterizeInArray::_MinArraySize#{min_array_size}"}
|
190
|
+
db.extend_datasets(mod)
|
191
|
+
end
|
109
192
|
end
|
110
193
|
end
|
@@ -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
|
|
@@ -257,7 +257,7 @@ END_MIG
|
|
257
257
|
gen.foreign_key(name, table, col_opts)
|
258
258
|
else
|
259
259
|
gen.column(name, type, col_opts)
|
260
|
-
if [Integer, :Bignum, Float, BigDecimal].include?(type) && schema[:db_type] =~ / unsigned\z/
|
260
|
+
if [Integer, :Bignum, Float, BigDecimal].include?(type) && schema[:db_type] =~ / unsigned\z/i
|
261
261
|
gen.check(Sequel::SQL::Identifier.new(name) >= 0)
|
262
262
|
end
|
263
263
|
end
|
@@ -414,6 +414,12 @@ module Sequel
|
|
414
414
|
false
|
415
415
|
end
|
416
416
|
|
417
|
+
# Hash value for the association reflection. This is precomputed to avoid
|
418
|
+
# concurrency issues at runtime.
|
419
|
+
def hash
|
420
|
+
self[:_hash]
|
421
|
+
end
|
422
|
+
|
417
423
|
# Initialize the associations cache for the current association for the given objects.
|
418
424
|
def initialize_association_cache(objects)
|
419
425
|
name = self[:name]
|
@@ -693,6 +699,9 @@ module Sequel
|
|
693
699
|
|
694
700
|
# The predicate condition to use for the eager_loader.
|
695
701
|
def eager_loading_predicate_condition(keys)
|
702
|
+
if transform = self[:eager_loading_predicate_transform]
|
703
|
+
keys = transform.call(keys, self)
|
704
|
+
end
|
696
705
|
{predicate_key=>keys}
|
697
706
|
end
|
698
707
|
|
@@ -759,7 +768,15 @@ module Sequel
|
|
759
768
|
def placeholder_eager_loader
|
760
769
|
cached_fetch(:placeholder_eager_loader) do
|
761
770
|
eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
|
762
|
-
|
771
|
+
arg = pl.arg
|
772
|
+
|
773
|
+
if transform = self[:eager_loading_predicate_transform]
|
774
|
+
arg = arg.transform do |v|
|
775
|
+
transform.call(v, self)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
|
763
780
|
end
|
764
781
|
end
|
765
782
|
end
|
@@ -1707,6 +1724,9 @@ module Sequel
|
|
1707
1724
|
# record should be populated.
|
1708
1725
|
# :eager_loader_key :: A symbol for the key column to use to populate the key_hash
|
1709
1726
|
# for the eager loader. Can be set to nil to not populate the key_hash.
|
1727
|
+
# :eager_loading_predicate_transform :: A callable object with which to transform the predicate key values used
|
1728
|
+
# when eager loading. Called with two arguments, the array of predicate key
|
1729
|
+
# values, and a the reflection for the association being eager loaded.
|
1710
1730
|
# :extend :: A module or array of modules to extend the dataset with.
|
1711
1731
|
# :filter_limit_strategy :: Determines the strategy used for enforcing limits and offsets when filtering by
|
1712
1732
|
# limited associations. Possible options are :window_function, :distinct_on, or
|
@@ -1769,6 +1789,9 @@ module Sequel
|
|
1769
1789
|
# Set to nil to not define a setter method for the association.
|
1770
1790
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1771
1791
|
# loading limited associations using the default :union strategy.
|
1792
|
+
# :use_placeholder_loader :: Whether to use a placeholder loader when eager loading the
|
1793
|
+
# association. Can be set to false to disable the use of a placeholder
|
1794
|
+
# loader if one would be used by default.
|
1772
1795
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
1773
1796
|
# === :many_to_one
|
1774
1797
|
# :key :: foreign key in current model's table that references
|
@@ -1891,7 +1914,7 @@ module Sequel
|
|
1891
1914
|
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
1915
|
end
|
1893
1916
|
|
1894
|
-
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1917
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph] unless opts.include?(:use_placeholder_loader)
|
1895
1918
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1896
1919
|
opts[:graph_join_type] ||= :left_outer
|
1897
1920
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
@@ -1914,6 +1937,8 @@ module Sequel
|
|
1914
1937
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1915
1938
|
opts.delete(:class) unless opts[:class]
|
1916
1939
|
|
1940
|
+
opts[:_hash] = [self, name].hash
|
1941
|
+
|
1917
1942
|
def_association(opts)
|
1918
1943
|
|
1919
1944
|
orig_opts.delete(:clone)
|
@@ -3591,7 +3616,7 @@ module Sequel
|
|
3591
3616
|
|
3592
3617
|
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3593
3618
|
def prepare_eager_load(a, reflections, eager_assoc)
|
3594
|
-
eager_load_data = {}
|
3619
|
+
eager_load_data = {}.compare_by_identity
|
3595
3620
|
|
3596
3621
|
# Key is foreign/primary key name symbol.
|
3597
3622
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -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
|
@@ -39,6 +39,9 @@ module Sequel
|
|
39
39
|
#
|
40
40
|
# Album.first.artist # no error
|
41
41
|
#
|
42
|
+
# This behavior of enabling +forbid_lazy_load+ automatically from dataset
|
43
|
+
# methods can be disabled using the plugin's +:allow_by_default+ option.
|
44
|
+
#
|
42
45
|
# You can allow lazy loading associations for an instance that it
|
43
46
|
# was previously forbidden for:
|
44
47
|
#
|
@@ -98,7 +101,17 @@ module Sequel
|
|
98
101
|
#
|
99
102
|
# # Make the Album class support forbidding lazy load
|
100
103
|
# Album.plugin :forbid_lazy_load
|
104
|
+
#
|
105
|
+
# # Let lazy loading be forbidden by object, but not automatically for any
|
106
|
+
# # object loaded via dataset.
|
107
|
+
# Album.plugin :forbid_lazy_load, allow_by_default: true
|
101
108
|
module ForbidLazyLoad
|
109
|
+
def self.apply(model, opts=OPTS)
|
110
|
+
unless opts[:allow_by_default]
|
111
|
+
model.send(:dataset_extend, ForbidByDefault, :create_class_methods=>false)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
102
115
|
# Error raised when attempting to lazy load an association when
|
103
116
|
# lazy loading has been forbidden.
|
104
117
|
class Error < StandardError
|
@@ -179,7 +192,7 @@ module Sequel
|
|
179
192
|
end
|
180
193
|
end
|
181
194
|
|
182
|
-
module
|
195
|
+
module ForbidByDefault
|
183
196
|
# Mark model instances retrieved in this call as forbidding lazy loading.
|
184
197
|
def each
|
185
198
|
if row_proc
|
@@ -188,11 +188,10 @@ module Sequel
|
|
188
188
|
|
189
189
|
# Create a new associated object with the given attributes, validate
|
190
190
|
# it when the parent is validated, and save it when the object is saved.
|
191
|
-
# Returns the object
|
191
|
+
# Returns the new object.
|
192
192
|
def nested_attributes_create(meta, attributes)
|
193
|
+
obj = nested_attributes_new(meta, attributes)
|
193
194
|
reflection = meta[:reflection]
|
194
|
-
obj = reflection.associated_class.new
|
195
|
-
nested_attributes_set_attributes(meta, obj, attributes)
|
196
195
|
delay_validate_associated_object(reflection, obj)
|
197
196
|
if reflection.returns_array?
|
198
197
|
public_send(reflection[:name]) << obj
|
@@ -254,7 +253,13 @@ module Sequel
|
|
254
253
|
end
|
255
254
|
obj
|
256
255
|
end
|
257
|
-
|
256
|
+
|
257
|
+
# Returns a new object of the associated class with the given attributes set.
|
258
|
+
def nested_attributes_new(meta, attributes)
|
259
|
+
obj = meta[:reflection].associated_class.new
|
260
|
+
nested_attributes_set_attributes(meta, obj, attributes)
|
261
|
+
end
|
262
|
+
|
258
263
|
# Set the fields in the obj based on the association, only allowing
|
259
264
|
# specific :fields if configured.
|
260
265
|
def nested_attributes_set_attributes(meta, obj, attributes)
|
@@ -135,7 +135,12 @@ module Sequel
|
|
135
135
|
raise Error, "No pg_auto_constraint_validations setup" unless file = @pg_auto_constraint_validations_cache_file
|
136
136
|
pg_auto_constraint_validations_cache = {}
|
137
137
|
@pg_auto_constraint_validations_cache.sort.each do |k, v|
|
138
|
-
|
138
|
+
h = {}
|
139
|
+
v.each do |k, entry|
|
140
|
+
entry = Hash[entry.sort] if entry.is_a?(Hash)
|
141
|
+
h[k] = entry
|
142
|
+
end
|
143
|
+
pg_auto_constraint_validations_cache[k] = h
|
139
144
|
end
|
140
145
|
File.open(file, 'wb'){|f| f.write(Marshal.dump(pg_auto_constraint_validations_cache))}
|
141
146
|
nil
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The pg_auto_validate_enums plugin implements automatic validations for
|
6
|
+
# enum columns, ensuring that enum columns have a valid value. With this
|
7
|
+
# plugin, trying to save with an invalid enum value results in
|
8
|
+
# Sequel::ValidationFailed before saving, instead of Sequel::DatabaseError
|
9
|
+
# (wrapping PG::InvalidTextRepresentation or similar exception) during saving.
|
10
|
+
#
|
11
|
+
# class Person < Sequel::Model
|
12
|
+
# # assume state enum column with allowed values active and inactive
|
13
|
+
# plugin :pg_auto_validate_enums
|
14
|
+
# end
|
15
|
+
# p = Person.new(state: "active").valid? # => true
|
16
|
+
# p = Person.new(state: "inactive").valid? # => true
|
17
|
+
# p = Person.new(state: "other").valid? # => false
|
18
|
+
#
|
19
|
+
# While you can load this into individual model classes, typical use would
|
20
|
+
# be to load it into Sequel::Model or the appropriate model base class,
|
21
|
+
# and have all models that inherit from that class automatically pick it up.
|
22
|
+
#
|
23
|
+
# This plugin depends on the validation_helpers plugin.
|
24
|
+
module PgAutoValidateEnums
|
25
|
+
# Load the validation_helpers plugin.
|
26
|
+
def self.apply(model, opts=OPTS)
|
27
|
+
model.plugin(:validation_helpers)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Load the pg_enum extension into the database, and reload the schema
|
31
|
+
# if it is already loaded. The opts given are used for the validates_includes
|
32
|
+
# validations (with allow_nil: true and from: :values enabled by default,
|
33
|
+
# to avoid issues with nullable enum columns and cases where the column
|
34
|
+
# method has been overridden.
|
35
|
+
def self.configure(model, opts=OPTS)
|
36
|
+
model.instance_exec do
|
37
|
+
db.extension(:pg_enum) unless @db.instance_variable_get(:@enum_labels)
|
38
|
+
if @db_schema
|
39
|
+
get_db_schema(true)
|
40
|
+
_get_pg_pg_auto_validate_enums_metadata
|
41
|
+
end
|
42
|
+
@pg_auto_validate_enums_opts = {allow_nil: true, from: :values}.merge!(opts).freeze
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
# Hash with enum column symbol values and arrays of valid string values.
|
48
|
+
attr_reader :pg_auto_validate_enums_metadata
|
49
|
+
|
50
|
+
# Options to pass to the validates_includes calls used by the plugin.
|
51
|
+
attr_reader :pg_auto_validate_enums_opts
|
52
|
+
|
53
|
+
Plugins.after_set_dataset(self, :_get_pg_pg_auto_validate_enums_metadata)
|
54
|
+
|
55
|
+
Plugins.inherited_instance_variables(self,
|
56
|
+
:@pg_auto_validate_enums_metadata=>nil,
|
57
|
+
:@pg_auto_validate_enums_opts=>nil)
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Parse the column schema to find columns with :enum_values entries,
|
62
|
+
# which will be used to setup validations.
|
63
|
+
def _get_pg_pg_auto_validate_enums_metadata
|
64
|
+
metadata = {}
|
65
|
+
@db_schema.each do |key, sch|
|
66
|
+
if enum_values = sch[:enum_values]
|
67
|
+
metadata[key] = enum_values
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@pg_auto_validate_enums_metadata = metadata.freeze
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
module InstanceMethods
|
75
|
+
# Validate that all of the model's enum columns have valid values.
|
76
|
+
def validate
|
77
|
+
super
|
78
|
+
|
79
|
+
klass = self.class
|
80
|
+
opts = klass.pg_auto_validate_enums_opts
|
81
|
+
klass.pg_auto_validate_enums_metadata.each do |column, values|
|
82
|
+
validates_includes(values, column, opts)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -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
|
@@ -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
|
@@ -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
|
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 = 91
|
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.91.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-04-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bigdecimal
|
@@ -371,6 +371,8 @@ files:
|
|
371
371
|
- lib/sequel/plugins/paged_operations.rb
|
372
372
|
- lib/sequel/plugins/pg_array_associations.rb
|
373
373
|
- lib/sequel/plugins/pg_auto_constraint_validations.rb
|
374
|
+
- lib/sequel/plugins/pg_auto_validate_enums.rb
|
375
|
+
- lib/sequel/plugins/pg_eager_any_typed_array.rb
|
374
376
|
- lib/sequel/plugins/pg_row.rb
|
375
377
|
- lib/sequel/plugins/pg_xmin_optimistic_locking.rb
|
376
378
|
- lib/sequel/plugins/prepared_statements.rb
|