sequel 5.30.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 +86 -0
- data/README.rdoc +1 -1
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +10 -5
- data/doc/code_order.rdoc +12 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/postgresql.rdoc +71 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +5 -5
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/oracle.rb +16 -16
- data/lib/sequel/adapters/shared/postgres.rb +169 -14
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +33 -6
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +2 -2
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +16 -10
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +15 -16
- data/lib/sequel/database/transactions.rb +7 -4
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/query.rb +5 -4
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/index_caching.rb +9 -7
- data/lib/sequel/extensions/integer64.rb +2 -0
- data/lib/sequel/extensions/migration.rb +1 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +7 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +6 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +5 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +10 -4
- data/lib/sequel/extensions/server_block.rb +3 -3
- 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.rb +2 -0
- data/lib/sequel/model/associations.rb +54 -25
- data/lib/sequel/model/base.rb +70 -57
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- 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 +10 -16
- data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +18 -2
|
@@ -40,12 +40,22 @@ module Sequel
|
|
|
40
40
|
def self.extended(db)
|
|
41
41
|
db.instance_exec do
|
|
42
42
|
extend_datasets(InetDatasetMethods)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
|
|
44
|
+
# :nocov:
|
|
45
|
+
if !defined?(SEQUEL_PG_VERSION_INTEGER) || SEQUEL_PG_VERSION_INTEGER >= 11300
|
|
46
|
+
# :nocov:
|
|
47
|
+
# sequel_pg 1.13.0+ will use inet/cidr conversion procs, but doing so is
|
|
48
|
+
# slower, so don't add the conversion procs if using sequel_pg 1.13.0+.
|
|
49
|
+
meth = IPAddr.method(:new)
|
|
50
|
+
add_conversion_proc(869, meth)
|
|
51
|
+
add_conversion_proc(650, meth)
|
|
52
|
+
if respond_to?(:register_array_type)
|
|
53
|
+
register_array_type('inet', :oid=>1041, :scalar_oid=>869)
|
|
54
|
+
register_array_type('cidr', :oid=>651, :scalar_oid=>650)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
46
58
|
if respond_to?(:register_array_type)
|
|
47
|
-
register_array_type('inet', :oid=>1041, :scalar_oid=>869)
|
|
48
|
-
register_array_type('cidr', :oid=>651, :scalar_oid=>650)
|
|
49
59
|
register_array_type('macaddr', :oid=>1040, :scalar_oid=>829)
|
|
50
60
|
end
|
|
51
61
|
@schema_type_classes[:ipaddr] = IPAddr
|
|
@@ -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]
|
|
@@ -304,6 +298,8 @@ module Sequel
|
|
|
304
298
|
end
|
|
305
299
|
|
|
306
300
|
module DatasetMethods
|
|
301
|
+
private
|
|
302
|
+
|
|
307
303
|
# Handle literalization of ruby Range objects, treating them as
|
|
308
304
|
# PostgreSQL ranges.
|
|
309
305
|
def literal_other_append(sql, v)
|
|
@@ -469,8 +465,10 @@ module Sequel
|
|
|
469
465
|
return @range if @range
|
|
470
466
|
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
|
471
467
|
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
|
468
|
+
# :nocov:
|
|
472
469
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
|
|
473
470
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
|
|
471
|
+
# :nocov:
|
|
474
472
|
@range = Range.new(self.begin, self.end, exclude_end?)
|
|
475
473
|
end
|
|
476
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.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The run_transaction_hooks extension allows for running after_commit or
|
|
4
|
+
# after_rollback extensions before commit or rollback. It then removes
|
|
5
|
+
# the hook after running it, so it will not be run twice.
|
|
6
|
+
#
|
|
7
|
+
# This extension should only be used in transactional tests where the
|
|
8
|
+
# transaction always rolls back, to test the behavior of the after_commit
|
|
9
|
+
# and after_rollback hooks. Any other usage is probably a bad idea.
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
#
|
|
13
|
+
# DB.extension :run_transaction_hooks
|
|
14
|
+
# x = 1
|
|
15
|
+
# DB.transaction(rollback: :always) do
|
|
16
|
+
# DB.after_rollback{x = 3}
|
|
17
|
+
# DB.after_commit{x = 2}
|
|
18
|
+
#
|
|
19
|
+
# x # => 1
|
|
20
|
+
# DB.run_after_rollback_hooks
|
|
21
|
+
# x # => 3
|
|
22
|
+
# DB.run_after_commit_hooks
|
|
23
|
+
# x # => 2
|
|
24
|
+
# end
|
|
25
|
+
# x # => 2
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
class Sequel::Database
|
|
29
|
+
module RunTransactionHooks
|
|
30
|
+
# Run all savepoint and transaction after_commit hooks for the current transaction,
|
|
31
|
+
# and remove the hooks after running them.
|
|
32
|
+
# Options:
|
|
33
|
+
# :server :: The server/shard to use.
|
|
34
|
+
def run_after_commit_hooks(opts=OPTS)
|
|
35
|
+
_run_transaction_hooks(:after_commit, opts)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Run all savepoint and transaction after_rollback hooks for the current transaction,
|
|
39
|
+
# and remove the hooks after running them.
|
|
40
|
+
# Options:
|
|
41
|
+
# :server :: The server/shard to use.
|
|
42
|
+
def run_after_rollback_hooks(opts=OPTS)
|
|
43
|
+
_run_transaction_hooks(:after_rollback, opts)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def _run_transaction_hooks(type, opts)
|
|
49
|
+
synchronize(opts[:server]) do |conn|
|
|
50
|
+
unless h = _trans(conn)
|
|
51
|
+
raise Sequel::Error, "Cannot call run_#{type}_hooks outside of a transaction"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if hooks = h[type]
|
|
55
|
+
hooks.each(&:call)
|
|
56
|
+
hooks.clear
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if (savepoints = h[:savepoints])
|
|
60
|
+
savepoints.each do |savepoint|
|
|
61
|
+
if hooks = savepoint[type]
|
|
62
|
+
hooks.each(&:call)
|
|
63
|
+
hooks.clear
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
register_extension(:run_transaction_hooks, RunTransactionHooks)
|
|
72
|
+
end
|
data/lib/sequel/extensions/s.rb
CHANGED
|
@@ -199,12 +199,18 @@ END_MIG
|
|
|
199
199
|
end
|
|
200
200
|
type = col_opts.delete(:type)
|
|
201
201
|
col_opts.delete(:size) if col_opts[:size].nil?
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
if schema[:generated]
|
|
203
|
+
if options[:same_db] && database_type == :postgres
|
|
204
|
+
col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
|
|
205
|
+
end
|
|
204
206
|
else
|
|
205
|
-
schema[:ruby_default]
|
|
207
|
+
col_opts[:default] = if schema[:ruby_default].nil?
|
|
208
|
+
column_schema_to_ruby_default_fallback(schema[:default], options)
|
|
209
|
+
else
|
|
210
|
+
schema[:ruby_default]
|
|
211
|
+
end
|
|
212
|
+
col_opts.delete(:default) if col_opts[:default].nil?
|
|
206
213
|
end
|
|
207
|
-
col_opts.delete(:default) if col_opts[:default].nil?
|
|
208
214
|
col_opts[:null] = false if schema[:allow_null] == false
|
|
209
215
|
if table = schema[:table]
|
|
210
216
|
[:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
|
|
@@ -143,13 +143,13 @@ module Sequel
|
|
|
143
143
|
|
|
144
144
|
# Make the given server the new default server for the current thread.
|
|
145
145
|
def set_default_server(default_server, read_only_server=default_server)
|
|
146
|
-
sync{(@default_servers[
|
|
146
|
+
sync{(@default_servers[Sequel.current] ||= [])} << [default_server, read_only_server]
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
# Remove the current default server for the current thread, restoring the
|
|
150
150
|
# previous default server.
|
|
151
151
|
def clear_default_server
|
|
152
|
-
t =
|
|
152
|
+
t = Sequel.current
|
|
153
153
|
a = sync{@default_servers[t]}
|
|
154
154
|
a.pop
|
|
155
155
|
sync{@default_servers.delete(t)} if a.empty?
|
|
@@ -157,7 +157,7 @@ module Sequel
|
|
|
157
157
|
|
|
158
158
|
# Use the server given to with_server for the given thread, if appropriate.
|
|
159
159
|
def pick_server(server)
|
|
160
|
-
a = sync{@default_servers[
|
|
160
|
+
a = sync{@default_servers[Sequel.current]}
|
|
161
161
|
if !a || a.empty?
|
|
162
162
|
super
|
|
163
163
|
else
|
|
@@ -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
|
data/lib/sequel/model.rb
CHANGED
|
@@ -69,7 +69,9 @@ module Sequel
|
|
|
69
69
|
require_relative "model/base"
|
|
70
70
|
require_relative "model/exceptions"
|
|
71
71
|
require_relative "model/errors"
|
|
72
|
+
# :nocov:
|
|
72
73
|
if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
|
|
74
|
+
# :nocov:
|
|
73
75
|
require_relative 'model/associations'
|
|
74
76
|
plugin Model::Associations
|
|
75
77
|
end
|
|
@@ -164,11 +164,11 @@ module Sequel
|
|
|
164
164
|
# range to return the object(s) at the correct offset/limit.
|
|
165
165
|
def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
|
|
166
166
|
name = self[:name]
|
|
167
|
+
return unless range = slice_range(limit_and_offset)
|
|
167
168
|
if returns_array?
|
|
168
|
-
range = slice_range(limit_and_offset)
|
|
169
169
|
rows.each{|o| o.associations[name] = o.associations[name][range] || []}
|
|
170
|
-
|
|
171
|
-
offset =
|
|
170
|
+
else
|
|
171
|
+
offset = range.begin
|
|
172
172
|
rows.each{|o| o.associations[name] = o.associations[name][offset]}
|
|
173
173
|
end
|
|
174
174
|
end
|
|
@@ -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
|
|
|
@@ -1244,7 +1253,9 @@ module Sequel
|
|
|
1244
1253
|
else
|
|
1245
1254
|
assoc_record.values.delete(left_key_alias)
|
|
1246
1255
|
end
|
|
1247
|
-
|
|
1256
|
+
|
|
1257
|
+
objects = h[hash_key]
|
|
1258
|
+
|
|
1248
1259
|
if assign_singular
|
|
1249
1260
|
objects.each do |object|
|
|
1250
1261
|
object.associations[name] ||= assoc_record
|
|
@@ -1791,11 +1802,12 @@ module Sequel
|
|
|
1791
1802
|
opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
|
1792
1803
|
|
|
1793
1804
|
opts[:block] = block if block
|
|
1794
|
-
|
|
1805
|
+
opts[:instance_specific] = true if orig_opts[:dataset]
|
|
1806
|
+
if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
|
|
1795
1807
|
# It's possible the association is instance specific, in that it depends on
|
|
1796
1808
|
# values other than the foreign key value. This needs to be checked for
|
|
1797
1809
|
# in certain places to disable optimizations.
|
|
1798
|
-
opts[:instance_specific] =
|
|
1810
|
+
opts[:instance_specific] = _association_instance_specific_default(name)
|
|
1799
1811
|
end
|
|
1800
1812
|
opts = assoc_class.new.merge!(opts)
|
|
1801
1813
|
|
|
@@ -1803,6 +1815,7 @@ module Sequel
|
|
|
1803
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]})")
|
|
1804
1816
|
end
|
|
1805
1817
|
|
|
1818
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
|
1806
1819
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
|
1807
1820
|
opts[:graph_join_type] ||= :left_outer
|
|
1808
1821
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
|
@@ -1899,6 +1912,12 @@ module Sequel
|
|
|
1899
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])
|
|
1900
1913
|
|
|
1901
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
|
|
1902
1921
|
|
|
1903
1922
|
# The module to use for the association's methods. Defaults to
|
|
1904
1923
|
# the overridable_methods_module.
|
|
@@ -1948,10 +1967,8 @@ module Sequel
|
|
|
1948
1967
|
if opts[:block]
|
|
1949
1968
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
|
1950
1969
|
end
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
|
1954
|
-
end
|
|
1970
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
|
1971
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
|
1955
1972
|
def_association_method(opts)
|
|
1956
1973
|
|
|
1957
1974
|
return if opts[:read_only]
|
|
@@ -2122,9 +2139,7 @@ module Sequel
|
|
|
2122
2139
|
|
|
2123
2140
|
eager_load_results(opts, eo) do |assoc_record|
|
|
2124
2141
|
hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
|
|
2125
|
-
|
|
2126
|
-
objects.each{|object| object.associations[name] = assoc_record}
|
|
2127
|
-
end
|
|
2142
|
+
h[hash_key].each{|object| object.associations[name] = assoc_record}
|
|
2128
2143
|
end
|
|
2129
2144
|
end
|
|
2130
2145
|
|
|
@@ -2171,7 +2186,7 @@ module Sequel
|
|
|
2171
2186
|
eager_load_results(opts, eo) do |assoc_record|
|
|
2172
2187
|
assoc_record.values.delete(delete_rn) if delete_rn
|
|
2173
2188
|
hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
|
|
2174
|
-
|
|
2189
|
+
objects = h[hash_key]
|
|
2175
2190
|
if assign_singular
|
|
2176
2191
|
objects.each do |object|
|
|
2177
2192
|
unless object.associations[name]
|
|
@@ -2966,8 +2981,8 @@ module Sequel
|
|
|
2966
2981
|
# dataset. If that association also has dependent associations, instead of a callable object,
|
|
2967
2982
|
# use a hash with the callable object being the key, and the dependent association(s) as the value.
|
|
2968
2983
|
#
|
|
2969
|
-
# You can specify an alias
|
|
2970
|
-
# an a Symbol for the
|
|
2984
|
+
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
|
2985
|
+
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
|
2971
2986
|
#
|
|
2972
2987
|
# Examples:
|
|
2973
2988
|
#
|
|
@@ -2983,6 +2998,14 @@ module Sequel
|
|
|
2983
2998
|
# # FROM albums
|
|
2984
2999
|
# # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
|
|
2985
3000
|
#
|
|
3001
|
+
# # For each album, eager_graph load the artist, using a specified alias
|
|
3002
|
+
# # and custom join type
|
|
3003
|
+
#
|
|
3004
|
+
# Album.eager_graph(Sequel[:artist].as(:a, join_type: :inner)).all
|
|
3005
|
+
# # SELECT ...
|
|
3006
|
+
# # FROM albums
|
|
3007
|
+
# # INNER JOIN artists AS a ON (a.id = albums.artist_id)
|
|
3008
|
+
#
|
|
2986
3009
|
# # For each album, eager_graph load the artist and genre
|
|
2987
3010
|
# Album.eager_graph(:artist, :genre).all
|
|
2988
3011
|
# Album.eager_graph(:artist).eager_graph(:genre).all
|
|
@@ -3056,6 +3079,8 @@ module Sequel
|
|
|
3056
3079
|
# significantly slower in some cases (perhaps even the majority of cases), so you should
|
|
3057
3080
|
# only use this if you have benchmarked that it is faster for your use cases.
|
|
3058
3081
|
def eager_graph_with_options(associations, opts=OPTS)
|
|
3082
|
+
return self if associations.empty?
|
|
3083
|
+
|
|
3059
3084
|
opts = opts.dup unless opts.frozen?
|
|
3060
3085
|
associations = [associations] unless associations.is_a?(Array)
|
|
3061
3086
|
ds = if eg = @opts[:eager_graph]
|
|
@@ -3125,11 +3150,16 @@ module Sequel
|
|
|
3125
3150
|
# ta :: table_alias used for the parent association
|
|
3126
3151
|
# requirements :: an array, used as a stack for requirements
|
|
3127
3152
|
# r :: association reflection for the current association, or an SQL::AliasedExpression
|
|
3128
|
-
# with the reflection as the expression
|
|
3153
|
+
# with the reflection as the expression, the alias base as the alias (or nil to
|
|
3154
|
+
# use the default alias), and an optional hash with a :join_type entry as the columns
|
|
3155
|
+
# to use a custom join type.
|
|
3129
3156
|
# *associations :: any associations dependent on this one
|
|
3130
3157
|
def eager_graph_association(ds, model, ta, requirements, r, *associations)
|
|
3131
3158
|
if r.is_a?(SQL::AliasedExpression)
|
|
3132
3159
|
alias_base = r.alias
|
|
3160
|
+
if r.columns.is_a?(Hash)
|
|
3161
|
+
join_type = r.columns[:join_type]
|
|
3162
|
+
end
|
|
3133
3163
|
r = r.expression
|
|
3134
3164
|
else
|
|
3135
3165
|
alias_base = r[:graph_alias_base]
|
|
@@ -3152,7 +3182,7 @@ module Sequel
|
|
|
3152
3182
|
raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
|
|
3153
3183
|
end
|
|
3154
3184
|
|
|
3155
|
-
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
|
|
3185
|
+
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
|
|
3156
3186
|
if r[:order_eager_graph] && (order = r.fetch(:graph_order, r[:order]))
|
|
3157
3187
|
ds = ds.order_append(*qualified_expression(order, assoc_table_alias))
|
|
3158
3188
|
end
|
|
@@ -3177,7 +3207,6 @@ module Sequel
|
|
|
3177
3207
|
# requirements :: an array, used as a stack for requirements
|
|
3178
3208
|
# *associations :: the associations to add to the graph
|
|
3179
3209
|
def eager_graph_associations(ds, model, ta, requirements, *associations)
|
|
3180
|
-
return ds if associations.empty?
|
|
3181
3210
|
associations.flatten.each do |association|
|
|
3182
3211
|
ds = case association
|
|
3183
3212
|
when Symbol, SQL::AliasedExpression
|
|
@@ -3307,7 +3336,7 @@ module Sequel
|
|
|
3307
3336
|
end
|
|
3308
3337
|
end
|
|
3309
3338
|
|
|
3310
|
-
SQL::AliasedExpression.new(check_association(model, expr), association.alias)
|
|
3339
|
+
SQL::AliasedExpression.new(check_association(model, expr), association.alias || expr, association.columns)
|
|
3311
3340
|
else
|
|
3312
3341
|
check_association(model, association)
|
|
3313
3342
|
end
|