sequel 5.34.0 → 5.35.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 +7 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/release_notes/5.35.0.txt +56 -0
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/sqlite.rb +8 -2
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/deprecated.rb +1 -1
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/migration.rb +0 -1
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +3 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +24 -7
- data/lib/sequel/model/base.rb +7 -3
- data/lib/sequel/plugins/association_pks.rb +3 -2
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -3
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements.rb +5 -11
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +8 -14
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ac0d24707f9029714f4e5258c484148d7dc10a733c0c33865d252141cd4cc2e
|
4
|
+
data.tar.gz: cdabb1bb51fdfe7b7a22df0a6a9102dd15366bbdd98100907b2e3d74db4e5878
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d87a7d026c51d8897c964cca01ad30384b853bdbbda42811ee7245ef669ac23c1093e572a0e414db5daeb84b8f417e63c14c3ac13aa0b12ac9eb8f801ea6e6c2
|
7
|
+
data.tar.gz: 3e788cabe74d01035b930ca699b5ebc763a6a34410c1b8a3a5075d5a276c5155355d9476303b6b606d1b0a74d6a535ca4fdca696cdc0b83e32658be45c8f946e
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 5.35.0 (2020-08-01)
|
2
|
+
|
3
|
+
* Recognize another disconnect error in the oracle adapter (sterlzbd) (#1705)
|
4
|
+
|
5
|
+
* Consider all associations with :dataset options as instance-specific associations (jeremyevans)
|
6
|
+
|
7
|
+
* Make Model.finalize_associations not break with instance-specific associations (jeremyevans)
|
8
|
+
|
9
|
+
* Make association placeholder loader consider block if instance_specific: false association option is used (jeremyevans)
|
10
|
+
|
11
|
+
* Copy composite unique constraints when emulating alter table operations on SQLite (jeremyevans) (#1704)
|
12
|
+
|
13
|
+
* Add instance_specific_default plugin for setting default association :instance_specific value, or warning/raising for cases where it is not specified (jeremyevans)
|
14
|
+
|
15
|
+
* Make Model.plugin issue deprecation warning if loading plugin with arguments and block if plugin does not accept arguments/block (jeremyevans)
|
16
|
+
|
17
|
+
* Make validation_class_methods consider all :if, :allow_missing, :allow_nil, and :allow_blank settings, instead of just the first (jeremyevans)
|
18
|
+
|
19
|
+
* Include hash entries with nil keys in Dataset#to_dot output in to_dot extension (jeremyevans)
|
20
|
+
|
21
|
+
* Remove unneeded conditionals from plugins and extensions (jeremyevans)
|
22
|
+
|
23
|
+
* Fix exception class in run_transaction_hooks extension if calling run_after_{commit,rollback}_hooks outside of a transaction (jeremyevans)
|
24
|
+
|
1
25
|
=== 5.34.0 (2020-07-01)
|
2
26
|
|
3
27
|
* Make eager_graph work correctly if called with no associations (jeremyevans)
|
data/doc/association_basics.rdoc
CHANGED
@@ -1676,11 +1676,16 @@ instances.
|
|
1676
1676
|
==== :instance_specific
|
1677
1677
|
|
1678
1678
|
This allows you to override the setting of whether the dataset contains instance
|
1679
|
-
specific code.
|
1679
|
+
specific code. If you are passing a block to the association,
|
1680
1680
|
Sequel sets this to true by default, which disables some optimizations that
|
1681
1681
|
would be invalid if the association is instance specific. If you know that the
|
1682
1682
|
block does not contain instance specific code, you can set this to false to
|
1683
|
-
reenable the optimizations.
|
1683
|
+
reenable the optimizations. Instance specific code is mostly commonly calling
|
1684
|
+
model instance methods inside an association block, but also
|
1685
|
+
includes cases where the association block can return different values based
|
1686
|
+
on the runtime environment, such as calls to <tt>Time.now</tt> in the block.
|
1687
|
+
Associations that use the :dataset option are always considered instance specific,
|
1688
|
+
even if explicitly specified otherwise.
|
1684
1689
|
|
1685
1690
|
==== :cartesian_product_number
|
1686
1691
|
|
data/doc/dataset_filtering.rdoc
CHANGED
@@ -36,8 +36,8 @@ Ranges (both inclusive and exclusive) can also be used:
|
|
36
36
|
|
37
37
|
If you need to select multiple items from a dataset, you can supply an array:
|
38
38
|
|
39
|
-
|
40
|
-
|
39
|
+
items.where(id: [1, 38, 47, 99]).sql
|
40
|
+
# "SELECT * FROM items WHERE (id IN (1, 38, 47, 99))"
|
41
41
|
|
42
42
|
== Filtering using expressions
|
43
43
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An instance_specific_default plugin has been added for setting the
|
4
|
+
default for the :instance_specific association option, or
|
5
|
+
warning/raises in cases where it is not specified. This allows
|
6
|
+
you to easily find associations that would be considering instance
|
7
|
+
specific by default, and mark them as not instance specific for
|
8
|
+
better performance.
|
9
|
+
|
10
|
+
= Other Improvements
|
11
|
+
|
12
|
+
* Setting the :instance_specific association option to false now
|
13
|
+
works correctly if the association uses a block. Associations
|
14
|
+
that set the :dataset option are now always considered instance
|
15
|
+
specific, even if the :instance_specific option is explicitly
|
16
|
+
passed.
|
17
|
+
|
18
|
+
* The validation_class_methods plugin now considers all :if,
|
19
|
+
:allow_missing, :allow_nil, and :allow_blank options. Previously,
|
20
|
+
it only considered the first of those options that was set.
|
21
|
+
|
22
|
+
* Model.finalize_associations no longer breaks if you have
|
23
|
+
instance-specific associations.
|
24
|
+
|
25
|
+
* Model.plugin now warns if you load the plugin with arguments or a
|
26
|
+
block if the plugin does not accept arguments or block. This is
|
27
|
+
because a future change to Sequel could break the call.
|
28
|
+
|
29
|
+
* When emulating unsupported alter table operations on SQLite, Sequel
|
30
|
+
now copies composite unique constraints unless the alter table
|
31
|
+
operation is the dropping of a unique constraint.
|
32
|
+
|
33
|
+
* Sequel now recognizes an additional disconnect error in the oracle
|
34
|
+
adapter.
|
35
|
+
|
36
|
+
* In the run_transaction_hooks extension, calling
|
37
|
+
run_after_{commit,rollback}_hooks now raises the correct exception
|
38
|
+
class.
|
39
|
+
|
40
|
+
* In the pg_range extension, conversion procs for the tsrange[] and
|
41
|
+
tstzrange[] types are not added unless the Database uses the
|
42
|
+
pg_array extension.
|
43
|
+
|
44
|
+
* Multiple unnecessary conditionals in plugins and extensions have
|
45
|
+
been removed.
|
46
|
+
|
47
|
+
* Sequel plugin and extension code now have 100% branch coverage.
|
48
|
+
|
49
|
+
* Sequel now avoids a statement not reached verbose warning in
|
50
|
+
Dataset#clone.
|
51
|
+
|
52
|
+
= Backwards Compatibility
|
53
|
+
|
54
|
+
* The output of Dataset#to_dot in the to_dot extension has changed
|
55
|
+
slightly, including hash entries with nil keys. These entries
|
56
|
+
were previously ignored.
|
@@ -14,7 +14,8 @@ module Sequel
|
|
14
14
|
# ORA-02396: exceeded maximum idle time, please connect again
|
15
15
|
# ORA-03113: end-of-file on communication channel
|
16
16
|
# ORA-03114: not connected to ORACLE
|
17
|
-
|
17
|
+
# ORA-03135: connection lost contact
|
18
|
+
CONNECTION_ERROR_CODES = [ 28, 1012, 2396, 3113, 3114, 3135 ].freeze
|
18
19
|
|
19
20
|
ORACLE_TYPES = {
|
20
21
|
:blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)},
|
@@ -269,6 +269,8 @@ module Sequel
|
|
269
269
|
else
|
270
270
|
duplicate_table(table, :no_foreign_keys=>true)
|
271
271
|
end
|
272
|
+
when :unique
|
273
|
+
duplicate_table(table, :no_unique=>true)
|
272
274
|
else
|
273
275
|
duplicate_table(table)
|
274
276
|
end
|
@@ -422,8 +424,12 @@ module Sequel
|
|
422
424
|
skip_indexes = []
|
423
425
|
indexes(table, :only_autocreated=>true).each do |name, h|
|
424
426
|
skip_indexes << name
|
425
|
-
if h[:
|
426
|
-
|
427
|
+
if h[:unique]
|
428
|
+
if h[:columns].length == 1
|
429
|
+
unique_columns.concat(h[:columns])
|
430
|
+
elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
|
431
|
+
constraints << {:type=>:unique, :columns=>h[:columns]}
|
432
|
+
end
|
427
433
|
end
|
428
434
|
end
|
429
435
|
unique_columns -= pks
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -81,7 +81,7 @@ module Sequel
|
|
81
81
|
# If the options changed include options in COLUMN_CHANGE_OPTS, the cached
|
82
82
|
# columns are deleted. This method should generally not be called
|
83
83
|
# directly by user code.
|
84
|
-
def clone(opts = (return self
|
84
|
+
def clone(opts = nil || (return self))
|
85
85
|
# return self used above because clone is called by almost all
|
86
86
|
# other query methods, and it is the fastest approach
|
87
87
|
c = super(:freeze=>false)
|
data/lib/sequel/deprecated.rb
CHANGED
@@ -39,7 +39,7 @@ module Sequel
|
|
39
39
|
# Print the message and possibly backtrace to the output.
|
40
40
|
def self.deprecate(method, instead=nil)
|
41
41
|
return unless output
|
42
|
-
message = instead ? "#{method} is deprecated and will be removed in Sequel
|
42
|
+
message = instead ? "#{method} is deprecated and will be removed in Sequel 6. #{instead}." : method
|
43
43
|
message = "#{prefix}#{message}" if prefix
|
44
44
|
output.puts(message)
|
45
45
|
case b = backtrace_filter
|
@@ -75,8 +75,7 @@ module Sequel
|
|
75
75
|
when SQL::Identifier
|
76
76
|
c.value.to_sym
|
77
77
|
when SQL::QualifiedIdentifier
|
78
|
-
|
79
|
-
col.is_a?(SQL::Identifier) ? col.value.to_sym : col.to_sym
|
78
|
+
c.column.to_sym
|
80
79
|
when SQL::AliasedExpression
|
81
80
|
a = c.alias
|
82
81
|
a.is_a?(SQL::Identifier) ? a.value.to_sym : a.to_sym
|
@@ -39,7 +39,9 @@
|
|
39
39
|
|
40
40
|
module Sequel
|
41
41
|
module DuplicateColumnsHandler
|
42
|
+
# :nocov:
|
42
43
|
CALLER_ARGS = (RUBY_VERSION >= '2.0' ? [0,1] : [0]).freeze
|
44
|
+
# :nocov:
|
43
45
|
|
44
46
|
# Customize handling of duplicate columns for this dataset.
|
45
47
|
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless block_given?; nil), &block)
|
@@ -518,7 +518,6 @@ module Sequel
|
|
518
518
|
def initialize(db, directory, opts=OPTS)
|
519
519
|
super
|
520
520
|
@current = opts[:current] || current_migration_version
|
521
|
-
raise(Error, "No current version available") unless current
|
522
521
|
|
523
522
|
latest_version = latest_migration_version
|
524
523
|
@target = if opts[:target]
|
@@ -157,7 +157,9 @@ module Sequel
|
|
157
157
|
else
|
158
158
|
Sequel.function(:hstore, self, wrap_array(arg))
|
159
159
|
end
|
160
|
+
# :nocov:
|
160
161
|
if Sequel.respond_to?(:hstore_op)
|
162
|
+
# :nocov:
|
161
163
|
v = Sequel.hstore_op(v)
|
162
164
|
end
|
163
165
|
v
|
@@ -283,7 +285,9 @@ module Sequel
|
|
283
285
|
end
|
284
286
|
end
|
285
287
|
|
288
|
+
# :nocov:
|
286
289
|
if defined?(PGArray)
|
290
|
+
# :nocov:
|
287
291
|
class PGArray
|
288
292
|
# Wrap the PGArray instance in an ArrayOp, allowing you to easily use
|
289
293
|
# the PostgreSQL array functions and operators with literal arrays.
|
@@ -189,7 +189,7 @@ module Sequel
|
|
189
189
|
if date < DATETIME_YEAR_1
|
190
190
|
date <<= ((date.year) * 24 - 12)
|
191
191
|
date = db.from_application_timestamp(date)
|
192
|
-
minutes = (date.
|
192
|
+
minutes = (date.offset * 1440).to_i
|
193
193
|
date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
|
194
194
|
else
|
195
195
|
super
|
@@ -41,7 +41,9 @@ module Sequel
|
|
41
41
|
db.instance_exec do
|
42
42
|
extend_datasets(InetDatasetMethods)
|
43
43
|
|
44
|
+
# :nocov:
|
44
45
|
if !defined?(SEQUEL_PG_VERSION_INTEGER) || SEQUEL_PG_VERSION_INTEGER >= 11300
|
46
|
+
# :nocov:
|
45
47
|
# sequel_pg 1.13.0+ will use inet/cidr conversion procs, but doing so is
|
46
48
|
# slower, so don't add the conversion procs if using sequel_pg 1.13.0+.
|
47
49
|
meth = IPAddr.method(:new)
|
@@ -554,7 +554,9 @@ module Sequel
|
|
554
554
|
end
|
555
555
|
end
|
556
556
|
|
557
|
+
# :nocov:
|
557
558
|
if defined?(JSONArray)
|
559
|
+
# :nocov:
|
558
560
|
class JSONArray
|
559
561
|
# Wrap the JSONArray instance in an JSONOp, allowing you to easily use
|
560
562
|
# the PostgreSQL json functions and operators with literal jsons.
|
@@ -158,7 +158,7 @@ module Sequel
|
|
158
158
|
procs = conversion_procs
|
159
159
|
add_conversion_proc(3908, Parser.new("tsrange", procs[1114]))
|
160
160
|
add_conversion_proc(3910, Parser.new("tstzrange", procs[1184]))
|
161
|
-
if defined?(PGArray::Creator)
|
161
|
+
if respond_to?(:register_array_type) && defined?(PGArray::Creator)
|
162
162
|
add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908]))
|
163
163
|
add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910]))
|
164
164
|
end
|
@@ -215,12 +215,6 @@ module Sequel
|
|
215
215
|
|
216
216
|
db_type = db_type.to_s.dup.freeze
|
217
217
|
|
218
|
-
if converter = opts[:converter]
|
219
|
-
raise Error, "can't provide both a block and :converter option to register" if block
|
220
|
-
else
|
221
|
-
converter = block
|
222
|
-
end
|
223
|
-
|
224
218
|
if soid
|
225
219
|
raise Error, "can't provide both a converter and :subtype_oid option to register" if has_converter
|
226
220
|
raise Error, "no conversion proc for :subtype_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
|
@@ -471,8 +465,10 @@ module Sequel
|
|
471
465
|
return @range if @range
|
472
466
|
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
473
467
|
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
468
|
+
# :nocov:
|
474
469
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
|
475
470
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
|
471
|
+
# :nocov:
|
476
472
|
@range = Range.new(self.begin, self.end, exclude_end?)
|
477
473
|
end
|
478
474
|
|
@@ -116,7 +116,9 @@ module Sequel
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
# :nocov:
|
119
120
|
if defined?(PGRange)
|
121
|
+
# :nocov:
|
120
122
|
class PGRange
|
121
123
|
# Wrap the PGRange instance in an RangeOp, allowing you to easily use
|
122
124
|
# the PostgreSQL range functions and operators with literal ranges.
|
@@ -48,7 +48,7 @@ class Sequel::Database
|
|
48
48
|
def _run_transaction_hooks(type, opts)
|
49
49
|
synchronize(opts[:server]) do |conn|
|
50
50
|
unless h = _trans(conn)
|
51
|
-
raise Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
51
|
+
raise Sequel::Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
52
52
|
end
|
53
53
|
|
54
54
|
if hooks = h[type]
|
data/lib/sequel/extensions/s.rb
CHANGED
@@ -53,7 +53,13 @@ module Sequel
|
|
53
53
|
# is given, it is used directly as the node or transition. Otherwise
|
54
54
|
# a node is created for the current object.
|
55
55
|
def dot(label, j=nil)
|
56
|
-
|
56
|
+
label = case label
|
57
|
+
when nil
|
58
|
+
"<nil>"
|
59
|
+
else
|
60
|
+
label.to_s
|
61
|
+
end
|
62
|
+
@dot << "#{j||@i} [label=#{label.inspect}];"
|
57
63
|
end
|
58
64
|
|
59
65
|
# Recursive method that parses all of Sequel's internal datastructures,
|
@@ -61,7 +67,7 @@ module Sequel
|
|
61
67
|
# structure.
|
62
68
|
def v(e, l)
|
63
69
|
@i += 1
|
64
|
-
dot(l, "#{@stack.last} -> #{@i}")
|
70
|
+
dot(l, "#{@stack.last} -> #{@i}")
|
65
71
|
@stack.push(@i)
|
66
72
|
case e
|
67
73
|
when LiteralString
|
@@ -144,7 +150,7 @@ module Sequel
|
|
144
150
|
dot "Dataset"
|
145
151
|
TO_DOT_OPTIONS.each do |k|
|
146
152
|
if val = e.opts[k]
|
147
|
-
v(val, k
|
153
|
+
v(val, k)
|
148
154
|
end
|
149
155
|
end
|
150
156
|
else
|
@@ -356,7 +356,7 @@ module Sequel
|
|
356
356
|
def finalize
|
357
357
|
return unless cache = self[:cache]
|
358
358
|
|
359
|
-
|
359
|
+
finalizer = proc do |meth, key|
|
360
360
|
next if has_key?(key)
|
361
361
|
|
362
362
|
# Allow calling private methods to make sure caching is done appropriately
|
@@ -364,6 +364,13 @@ module Sequel
|
|
364
364
|
self[key] = cache.delete(key) if cache.has_key?(key)
|
365
365
|
end
|
366
366
|
|
367
|
+
finalize_settings.each(&finalizer)
|
368
|
+
|
369
|
+
unless self[:instance_specific]
|
370
|
+
finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
|
371
|
+
finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
|
372
|
+
end
|
373
|
+
|
367
374
|
nil
|
368
375
|
end
|
369
376
|
|
@@ -371,9 +378,7 @@ module Sequel
|
|
371
378
|
FINALIZE_SETTINGS = {
|
372
379
|
:associated_class=>:class,
|
373
380
|
:associated_dataset=>:_dataset,
|
374
|
-
:associated_eager_dataset=>:associated_eager_dataset,
|
375
381
|
:eager_limit_strategy=>:_eager_limit_strategy,
|
376
|
-
:filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
|
377
382
|
:placeholder_loader=>:placeholder_loader,
|
378
383
|
:predicate_key=>:predicate_key,
|
379
384
|
:predicate_keys=>:predicate_keys,
|
@@ -432,7 +437,11 @@ module Sequel
|
|
432
437
|
if use_placeholder_loader?
|
433
438
|
cached_fetch(:placeholder_loader) do
|
434
439
|
Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
|
435
|
-
ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
440
|
+
ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
441
|
+
if self[:block]
|
442
|
+
ds = self[:block].call(ds)
|
443
|
+
end
|
444
|
+
ds
|
436
445
|
end
|
437
446
|
end
|
438
447
|
end
|
@@ -796,7 +805,7 @@ module Sequel
|
|
796
805
|
|
797
806
|
# Whether the placeholder loader can be used to load the association.
|
798
807
|
def use_placeholder_loader?
|
799
|
-
|
808
|
+
self[:use_placeholder_loader]
|
800
809
|
end
|
801
810
|
end
|
802
811
|
|
@@ -1793,11 +1802,12 @@ module Sequel
|
|
1793
1802
|
opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
1794
1803
|
|
1795
1804
|
opts[:block] = block if block
|
1796
|
-
|
1805
|
+
opts[:instance_specific] = true if orig_opts[:dataset]
|
1806
|
+
if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
|
1797
1807
|
# It's possible the association is instance specific, in that it depends on
|
1798
1808
|
# values other than the foreign key value. This needs to be checked for
|
1799
1809
|
# in certain places to disable optimizations.
|
1800
|
-
opts[:instance_specific] =
|
1810
|
+
opts[:instance_specific] = _association_instance_specific_default(name)
|
1801
1811
|
end
|
1802
1812
|
opts = assoc_class.new.merge!(opts)
|
1803
1813
|
|
@@ -1805,6 +1815,7 @@ module Sequel
|
|
1805
1815
|
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]})")
|
1806
1816
|
end
|
1807
1817
|
|
1818
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1808
1819
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1809
1820
|
opts[:graph_join_type] ||= :left_outer
|
1810
1821
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
@@ -1901,6 +1912,12 @@ module Sequel
|
|
1901
1912
|
Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
|
1902
1913
|
|
1903
1914
|
private
|
1915
|
+
|
1916
|
+
# The default value for the instance_specific option, if the association
|
1917
|
+
# could be instance specific and the :instance_specific option is not specified.
|
1918
|
+
def _association_instance_specific_default(_)
|
1919
|
+
true
|
1920
|
+
end
|
1904
1921
|
|
1905
1922
|
# The module to use for the association's methods. Defaults to
|
1906
1923
|
# the overridable_methods_module.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -491,6 +491,11 @@ module Sequel
|
|
491
491
|
# the module using a the camelized plugin name under Sequel::Plugins.
|
492
492
|
def plugin(plugin, *args, &block)
|
493
493
|
m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
|
494
|
+
|
495
|
+
if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
|
496
|
+
Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
|
497
|
+
end
|
498
|
+
|
494
499
|
unless @plugins.include?(m)
|
495
500
|
@plugins << m
|
496
501
|
m.apply(self, *args, &block) if m.respond_to?(:apply)
|
@@ -500,6 +505,7 @@ module Sequel
|
|
500
505
|
dataset_extend(m::DatasetMethods, :create_class_methods=>false)
|
501
506
|
end
|
502
507
|
end
|
508
|
+
|
503
509
|
m.configure(self, *args, &block) if m.respond_to?(:configure)
|
504
510
|
end
|
505
511
|
|
@@ -632,8 +638,7 @@ module Sequel
|
|
632
638
|
|
633
639
|
# Cache of setter methods to allow by default, in order to speed up mass assignment.
|
634
640
|
def setter_methods
|
635
|
-
|
636
|
-
@setter_methods = get_setter_methods
|
641
|
+
@setter_methods || (@setter_methods = get_setter_methods)
|
637
642
|
end
|
638
643
|
|
639
644
|
# Returns name of primary table for the dataset. If the table for the dataset
|
@@ -1988,7 +1993,6 @@ module Sequel
|
|
1988
1993
|
|
1989
1994
|
# Get the ruby class or classes related to the given column's type.
|
1990
1995
|
def schema_type_class(column)
|
1991
|
-
# SEQUEL6: Remove
|
1992
1996
|
if (sch = db_schema[column]) && (type = sch[:type])
|
1993
1997
|
db.schema_type_class(type)
|
1994
1998
|
end
|
@@ -295,9 +295,10 @@ module Sequel
|
|
295
295
|
|
296
296
|
if primary_key.is_a?(Array)
|
297
297
|
if (cols = sch.values_at(*klass.primary_key)).all? && (convs = cols.map{|c| c[:type] == :integer}).all?
|
298
|
+
db = model.db
|
298
299
|
pks.map do |cpk|
|
299
|
-
cpk.
|
300
|
-
|
300
|
+
cpk.map do |pk|
|
301
|
+
db.typecast_value(:integer, pk)
|
301
302
|
end
|
302
303
|
end
|
303
304
|
else
|
@@ -58,8 +58,7 @@ module Sequel
|
|
58
58
|
# restricted_columns.
|
59
59
|
def get_setter_methods
|
60
60
|
meths = super
|
61
|
-
|
62
|
-
if (!defined?(::Sequel::Plugins::WhitelistSecurity) || !plugins.include?(::Sequel::Plugins::WhitelistSecurity) || !allowed_columns) && restricted_columns
|
61
|
+
if (!defined?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !is_a?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !allowed_columns) && restricted_columns
|
63
62
|
meths -= restricted_columns.map{|x| "#{x}="}
|
64
63
|
end
|
65
64
|
meths
|
@@ -289,7 +289,7 @@ module Sequel
|
|
289
289
|
|
290
290
|
# The name of the most recently joined table.
|
291
291
|
def cti_table_name
|
292
|
-
cti_tables
|
292
|
+
cti_tables.last
|
293
293
|
end
|
294
294
|
|
295
295
|
# The model class for the given key value.
|
@@ -310,7 +310,7 @@ module Sequel
|
|
310
310
|
# Set table if this is a class table inheritance
|
311
311
|
table = nil
|
312
312
|
columns = nil
|
313
|
-
if
|
313
|
+
if n = subclass.name
|
314
314
|
if table = cti_table_map[n.to_sym]
|
315
315
|
columns = db.schema(table).map(&:first)
|
316
316
|
else
|
@@ -417,7 +417,7 @@ module Sequel
|
|
417
417
|
@values[primary_key] ||= nid
|
418
418
|
end
|
419
419
|
end
|
420
|
-
|
420
|
+
@values[primary_key]
|
421
421
|
end
|
422
422
|
|
423
423
|
# Update rows in all backing tables, using the columns in each table.
|
@@ -111,7 +111,9 @@ module Sequel
|
|
111
111
|
# an association, allow lazy loading that association, since the
|
112
112
|
# lazy association load will use a hash table lookup and not a query.
|
113
113
|
def allow_lazy_load_for_static_cache_associations
|
114
|
+
# :nocov:
|
114
115
|
if defined?(::Sequel::Plugins::StaticCache::ClassMethods)
|
116
|
+
# :nocov:
|
115
117
|
@association_reflections.each_value do |ref|
|
116
118
|
if ref.associated_class.is_a?(::Sequel::Plugins::StaticCache::ClassMethods)
|
117
119
|
ref[:forbid_lazy_load] = false
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The instance_specific_default plugin exists to make it easier to use a
|
6
|
+
# global :instance_specific association option, or to warn or raise when Sequel
|
7
|
+
# has to guess which value to use :instance_specific option (Sequel defaults to
|
8
|
+
# guessing true as that is the conservative setting). It is helpful to
|
9
|
+
# use this plugin, particularly with the :warn or :raise settings, to determine
|
10
|
+
# which associations should have :instance_specific set. Setting the
|
11
|
+
# :instance_specific to false for associations that are not instance specific
|
12
|
+
# can improve performance.
|
13
|
+
#
|
14
|
+
# Associations are instance-specific if their block calls
|
15
|
+
# a model instance method, or where the value of the block varies
|
16
|
+
# based on runtime state, and the variance is outside of a delayed evaluation.
|
17
|
+
# For example, with the following three associations:
|
18
|
+
#
|
19
|
+
# Album.one_to_one :first_track, class: :Track do |ds|
|
20
|
+
# ds.where(number: 1)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Album.one_to_one :last_track, class: :Track do |ds|
|
24
|
+
# ds.where(number: num_tracks)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Album.one_to_many :recent_tracks, class: :Track do |ds|
|
28
|
+
# ds.where{date_updated > Date.today - 10}
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# +first_track+ is not instance specific, but +last_track+ and +recent_tracks+ are.
|
32
|
+
# +last_trac+ is because the +num_tracks+ call in the block is calling
|
33
|
+
# <tt>Album#num_tracks</tt>. +recent_tracks+ is because the value will change over
|
34
|
+
# time. This plugin allows you to find these cases, and set the :instance_specific
|
35
|
+
# option appropriately for them:
|
36
|
+
#
|
37
|
+
# Album.one_to_one :first_track, class: :Track, instance_specific: false do |ds|
|
38
|
+
# ds.where(number: 1)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# Album.one_to_one :last_track, class: :Track, instance_specific: true do |ds|
|
42
|
+
# ds.where(number: num_tracks)
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Album.one_to_many :recent_tracks, class: :Track, instance_specific: true do |ds|
|
46
|
+
# ds.where{date_updated > Date.today - 10}
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# For the +recent_tracks+ association, instead of marking it instance_specific, you
|
50
|
+
# could also use a delayed evaluation, since it doesn't actually contain
|
51
|
+
# instance-specific code:
|
52
|
+
#
|
53
|
+
# Album.one_to_many :recent_tracks, class: :Track, instance_specific: false do |ds|
|
54
|
+
# ds.where{date_updated > Sequel.delay{Date.today - 10}}
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# Possible arguments to provide when loading the plugin:
|
58
|
+
#
|
59
|
+
# true :: Set the :instance_specific option to true
|
60
|
+
# false :: Set the :instance_specific option to false
|
61
|
+
# :default :: Call super to set the :instance_specific option
|
62
|
+
# :warn :: Emit a warning before calling super to set the :instance_specific option
|
63
|
+
# :raise :: Raise a Sequel::Error if an :instance_specific option is not provided and
|
64
|
+
# an association could be instance-specific.
|
65
|
+
#
|
66
|
+
# Note that this plugin only affects associations which could be instance
|
67
|
+
# specific (those with blocks), where the :instance_specific option was not
|
68
|
+
# specified when the association was created.
|
69
|
+
#
|
70
|
+
# Usage:
|
71
|
+
#
|
72
|
+
# # Set how to handle associations that could be instance specific
|
73
|
+
# # but did not specify an :instance_specific option, for all subclasses
|
74
|
+
# # (set before creating subclasses).
|
75
|
+
# Sequel::Model.plugin :instance_specific_default, :warn
|
76
|
+
#
|
77
|
+
# # Set how to handle associations that could be instance specific
|
78
|
+
# # but did not specify an :instance_specific option, for the Album class
|
79
|
+
# Album.plugin :instance_specific_default, :warn
|
80
|
+
module InstanceSpecificDefault
|
81
|
+
# Set how to handle associations that could be instance specific but did
|
82
|
+
# not specify an :instance_specific value.
|
83
|
+
def self.configure(model, default)
|
84
|
+
model.instance_variable_set(:@instance_specific_default, default)
|
85
|
+
end
|
86
|
+
|
87
|
+
module ClassMethods
|
88
|
+
Plugins.inherited_instance_variables(self, :@instance_specific_default=>nil)
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Return the appropriate :instance_specific value, or warn or raise if
|
93
|
+
# configured.
|
94
|
+
def _association_instance_specific_default(name)
|
95
|
+
case @instance_specific_default
|
96
|
+
when true, false
|
97
|
+
return @instance_specific_default
|
98
|
+
when :default
|
99
|
+
# nothing
|
100
|
+
when :warn
|
101
|
+
warn("possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})", :uplevel => 3)
|
102
|
+
when :raise
|
103
|
+
raise Sequel::Error, "possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})"
|
104
|
+
else
|
105
|
+
raise Sequel::Error, "invalid value passed to instance_specific_default plugin: #{@instance_specific_default.inspect}"
|
106
|
+
end
|
107
|
+
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -98,7 +98,7 @@ module Sequel
|
|
98
98
|
end
|
99
99
|
|
100
100
|
if retrieved_with
|
101
|
-
|
101
|
+
primary_key = model.primary_key
|
102
102
|
composite_pk = true if primary_key.is_a?(Array)
|
103
103
|
id_map = {}
|
104
104
|
retrieved_with.each{|o| id_map[o.pk] = o unless o.values.has_key?(a) || o.frozen?}
|
@@ -341,10 +341,9 @@ module Sequel
|
|
341
341
|
eo[:loader] = false
|
342
342
|
|
343
343
|
eager_load_results(opts, eo) do |assoc_record|
|
344
|
-
if pks
|
344
|
+
if pks = assoc_record.get_column_value(key)
|
345
345
|
pks.each do |pkv|
|
346
|
-
|
347
|
-
objects.each do |object|
|
346
|
+
id_map[pkv].each do |object|
|
348
347
|
object.associations[name].push(assoc_record)
|
349
348
|
end
|
350
349
|
end
|
@@ -41,11 +41,9 @@ module Sequel
|
|
41
41
|
# Create a prepared statement, but modify the SQL used so that the model's columns are explicitly
|
42
42
|
# selected instead of using *, assuming that the dataset selects from a single table.
|
43
43
|
def prepare_explicit_statement(ds, type, vals=OPTS)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if f && f.length == 1 && !ds.opts[:join] && (!s || s.empty?)
|
48
|
-
ds = ds.public_send(meth, *columns.map{|c| Sequel.identifier(c)})
|
44
|
+
s = ds.opts[:returning]
|
45
|
+
if !s || s.empty?
|
46
|
+
ds = ds.returning(*columns.map{|c| Sequel.identifier(c)})
|
49
47
|
end
|
50
48
|
|
51
49
|
prepare_statement(ds, type, vals)
|
@@ -70,9 +68,7 @@ module Sequel
|
|
70
68
|
# Return a prepared statement that can be used to insert a row using the given columns
|
71
69
|
# and return that column values for the row created.
|
72
70
|
def prepared_insert_select(cols)
|
73
|
-
|
74
|
-
cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
|
75
|
-
end
|
71
|
+
cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
|
76
72
|
end
|
77
73
|
|
78
74
|
# Return an array of two element arrays with the column symbol as the first entry and the
|
@@ -138,9 +134,7 @@ module Sequel
|
|
138
134
|
# and return the new column values.
|
139
135
|
def _insert_select_raw(ds)
|
140
136
|
if use_prepared_statements_for?(:insert_select)
|
141
|
-
|
142
|
-
_set_prepared_statement_server(ps).call(@values)
|
143
|
-
end
|
137
|
+
_set_prepared_statement_server(model.send(:prepared_insert_select, @values.keys)).call(@values)
|
144
138
|
else
|
145
139
|
super
|
146
140
|
end
|
@@ -66,9 +66,7 @@ module Sequel
|
|
66
66
|
# Merge the current values into the default values to reduce the number
|
67
67
|
# of free columns.
|
68
68
|
def before_create
|
69
|
-
|
70
|
-
@values = v.merge(values)
|
71
|
-
end
|
69
|
+
@values = model.prepared_statements_column_defaults.merge(@values)
|
72
70
|
super
|
73
71
|
end
|
74
72
|
|
@@ -194,21 +194,17 @@ module Sequel
|
|
194
194
|
ds = ds.select_append(ka) unless ds.opts[:select] == nil
|
195
195
|
model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
|
196
196
|
opk = prkey_conv[obj]
|
197
|
-
if parent_map
|
198
|
-
|
199
|
-
|
200
|
-
obj = idm_obj
|
201
|
-
end
|
197
|
+
if idm_obj = parent_map[opk]
|
198
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
199
|
+
obj = idm_obj
|
202
200
|
else
|
203
201
|
obj.associations[parent] = nil
|
204
202
|
parent_map[opk] = obj
|
205
203
|
(children_map[key_conv[obj]] ||= []) << obj
|
206
204
|
end
|
207
205
|
|
208
|
-
|
209
|
-
|
210
|
-
root.associations[ancestors] << obj
|
211
|
-
end
|
206
|
+
id_map[extract_key_alias[obj]].each do |root|
|
207
|
+
root.associations[ancestors] << obj
|
212
208
|
end
|
213
209
|
end
|
214
210
|
parent_map.each do |parent_id, obj|
|
@@ -306,11 +302,9 @@ module Sequel
|
|
306
302
|
end
|
307
303
|
|
308
304
|
opk = prkey_conv[obj]
|
309
|
-
if parent_map
|
310
|
-
|
311
|
-
|
312
|
-
obj = idm_obj
|
313
|
-
end
|
305
|
+
if idm_obj = parent_map[opk]
|
306
|
+
key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
|
307
|
+
obj = idm_obj
|
314
308
|
else
|
315
309
|
obj.associations[childrena] = [] unless no_cache
|
316
310
|
parent_map[opk] = obj
|
@@ -28,7 +28,7 @@ module Sequel
|
|
28
28
|
model.plugin(:input_transformer, :string_stripper){|v| (v.is_a?(String) && !v.is_a?(SQL::Blob)) ? v.strip : v}
|
29
29
|
end
|
30
30
|
def self.configure(model)
|
31
|
-
model.
|
31
|
+
model.send(:set_skipped_string_stripping_columns)
|
32
32
|
end
|
33
33
|
|
34
34
|
module ClassMethods
|
@@ -194,7 +194,11 @@ module Sequel
|
|
194
194
|
def validates_each(*atts, &block)
|
195
195
|
opts = extract_options!(atts)
|
196
196
|
blank_meth = db.method(:blank_object?).to_proc
|
197
|
-
|
197
|
+
i = opts[:if]
|
198
|
+
am = opts[:allow_missing]
|
199
|
+
an = opts[:allow_nil]
|
200
|
+
ab = opts[:allow_blank]
|
201
|
+
blk = if i || am || an || ab
|
198
202
|
if i.is_a?(Proc)
|
199
203
|
i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i)
|
200
204
|
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 = 35
|
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.35.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: 2020-
|
11
|
+
date: 2020-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -182,6 +182,7 @@ extra_rdoc_files:
|
|
182
182
|
- doc/release_notes/5.32.0.txt
|
183
183
|
- doc/release_notes/5.33.0.txt
|
184
184
|
- doc/release_notes/5.34.0.txt
|
185
|
+
- doc/release_notes/5.35.0.txt
|
185
186
|
files:
|
186
187
|
- CHANGELOG
|
187
188
|
- MIT-LICENSE
|
@@ -237,6 +238,7 @@ files:
|
|
237
238
|
- doc/release_notes/5.32.0.txt
|
238
239
|
- doc/release_notes/5.33.0.txt
|
239
240
|
- doc/release_notes/5.34.0.txt
|
241
|
+
- doc/release_notes/5.35.0.txt
|
240
242
|
- doc/release_notes/5.4.0.txt
|
241
243
|
- doc/release_notes/5.5.0.txt
|
242
244
|
- doc/release_notes/5.6.0.txt
|
@@ -462,6 +464,7 @@ files:
|
|
462
464
|
- lib/sequel/plugins/insert_returning_select.rb
|
463
465
|
- lib/sequel/plugins/instance_filters.rb
|
464
466
|
- lib/sequel/plugins/instance_hooks.rb
|
467
|
+
- lib/sequel/plugins/instance_specific_default.rb
|
465
468
|
- lib/sequel/plugins/inverted_subsets.rb
|
466
469
|
- lib/sequel/plugins/json_serializer.rb
|
467
470
|
- lib/sequel/plugins/lazy_attributes.rb
|