sequel 3.31.0 → 3.32.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +74 -30
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/schema_modification.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/db2.rb +7 -0
- data/lib/sequel/adapters/jdbc/derby.rb +13 -0
- data/lib/sequel/adapters/jdbc/h2.rb +10 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +7 -0
- data/lib/sequel/adapters/mock.rb +4 -0
- data/lib/sequel/adapters/mysql.rb +3 -0
- data/lib/sequel/adapters/oracle.rb +7 -3
- data/lib/sequel/adapters/shared/db2.rb +9 -2
- data/lib/sequel/adapters/shared/mssql.rb +48 -2
- data/lib/sequel/adapters/shared/mysql.rb +24 -4
- data/lib/sequel/adapters/shared/oracle.rb +7 -6
- data/lib/sequel/adapters/shared/progress.rb +1 -1
- data/lib/sequel/adapters/shared/sqlite.rb +16 -10
- data/lib/sequel/core.rb +22 -0
- data/lib/sequel/database/query.rb +13 -4
- data/lib/sequel/dataset/actions.rb +20 -11
- data/lib/sequel/dataset/mutation.rb +7 -1
- data/lib/sequel/dataset/prepared_statements.rb +11 -0
- data/lib/sequel/dataset/sql.rb +21 -24
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/model.rb +5 -2
- data/lib/sequel/model/associations.rb +70 -16
- data/lib/sequel/model/base.rb +11 -6
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/composition.rb +43 -10
- data/lib/sequel/plugins/many_through_many.rb +4 -1
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- data/lib/sequel/plugins/serialization.rb +13 -8
- data/lib/sequel/plugins/serialization_modification_detection.rb +22 -10
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +33 -10
- data/spec/adapters/mysql_spec.rb +111 -91
- data/spec/adapters/oracle_spec.rb +18 -0
- data/spec/core/database_spec.rb +1 -0
- data/spec/core/dataset_spec.rb +110 -15
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/many_through_many_spec.rb +14 -14
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -1
- data/spec/extensions/serialization_spec.rb +9 -0
- data/spec/integration/associations_test.rb +278 -154
- data/spec/integration/dataset_test.rb +39 -2
- data/spec/integration/plugin_test.rb +63 -3
- data/spec/integration/prepared_statement_test.rb +10 -3
- data/spec/integration/schema_test.rb +61 -14
- data/spec/integration/transaction_test.rb +10 -0
- data/spec/model/associations_spec.rb +170 -80
- data/spec/model/hooks_spec.rb +40 -0
- metadata +4 -2
@@ -431,6 +431,10 @@ module Sequel
|
|
431
431
|
#
|
432
432
|
# DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
|
433
433
|
# # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
|
434
|
+
#
|
435
|
+
# When using this method, you must be sure that each expression has an alias
|
436
|
+
# that Sequel can determine. Usually you can do this by calling the #as method
|
437
|
+
# on the expression and providing an alias.
|
434
438
|
def select_hash(key_column, value_column)
|
435
439
|
if key_column.is_a?(Array)
|
436
440
|
if value_column.is_a?(Array)
|
@@ -461,6 +465,10 @@ module Sequel
|
|
461
465
|
#
|
462
466
|
# DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
|
463
467
|
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
468
|
+
#
|
469
|
+
# If you provide an array of expressions, you must be sure that each entry
|
470
|
+
# in the array has an alias that Sequel can determine. Usually you can do this
|
471
|
+
# by calling the #as method on the expression and providing an alias.
|
464
472
|
def select_map(column=nil, &block)
|
465
473
|
_select_map(column, false, &block)
|
466
474
|
end
|
@@ -478,6 +486,10 @@ module Sequel
|
|
478
486
|
#
|
479
487
|
# DB[:table].select_order_map([:id, :name]) # SELECT id, name FROM table ORDER BY id, name
|
480
488
|
# # => [[1, 'A'], [2, 'B'], [3, 'C'], ...]
|
489
|
+
#
|
490
|
+
# If you provide an array of expressions, you must be sure that each entry
|
491
|
+
# in the array has an alias that Sequel can determine. Usually you can do this
|
492
|
+
# by calling the #as method on the expression and providing an alias.
|
481
493
|
def select_order_map(column=nil, &block)
|
482
494
|
_select_map(column, true, &block)
|
483
495
|
end
|
@@ -636,17 +648,12 @@ module Sequel
|
|
636
648
|
# Internals of +select_map+ and +select_order_map+
|
637
649
|
def _select_map(column, order, &block)
|
638
650
|
ds = naked.ungraphed
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
else
|
646
|
-
ds = ds.select(&block)
|
647
|
-
ds = ds.order(&block) if order
|
648
|
-
end
|
649
|
-
if column.is_a?(Array)
|
651
|
+
columns = Array(column)
|
652
|
+
virtual_row_columns(columns, block)
|
653
|
+
select_cols = order ? columns.map{|c| c.is_a?(SQL::OrderedExpression) ? c.expression : c} : columns
|
654
|
+
ds = ds.select(*select_cols)
|
655
|
+
ds = ds.order(*columns.map{|c| unaliased_identifier(c)}) if order
|
656
|
+
if column.is_a?(Array) || (columns.length > 1)
|
650
657
|
ds._select_map_multiple(select_cols.map{|c| hash_key_symbol(c)})
|
651
658
|
else
|
652
659
|
ds._select_map_single
|
@@ -694,6 +701,8 @@ module Sequel
|
|
694
701
|
hash_key_symbol(s.column)
|
695
702
|
when SQL::AliasedExpression
|
696
703
|
hash_key_symbol(s.aliaz)
|
704
|
+
when String
|
705
|
+
s.to_sym
|
697
706
|
else
|
698
707
|
raise(Error, "#{s.inspect} is not supported, should be a Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier, or SQL::AliasedExpression")
|
699
708
|
end
|
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
# ---------------------
|
7
7
|
|
8
8
|
# All methods that should have a ! method added that modifies the receiver.
|
9
|
-
MUTATION_METHODS = QUERY_METHODS
|
9
|
+
MUTATION_METHODS = QUERY_METHODS - [:paginate, :naked]
|
10
10
|
|
11
11
|
# Setup mutation (e.g. filter!) methods. These operate the same as the
|
12
12
|
# non-! methods, but replace the options of the current dataset with the
|
@@ -39,6 +39,12 @@ module Sequel
|
|
39
39
|
instance_eval("def #{meth}!(*args, &block); mutation_method(:#{meth}, *args, &block) end", __FILE__, __LINE__)
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Remove the row_proc from the current dataset.
|
44
|
+
def naked!
|
45
|
+
self.row_proc = nil
|
46
|
+
self
|
47
|
+
end
|
42
48
|
|
43
49
|
private
|
44
50
|
|
@@ -77,6 +77,7 @@ module Sequel
|
|
77
77
|
def prepared_sql
|
78
78
|
case @prepared_type
|
79
79
|
when :select, :all
|
80
|
+
# Most common scenario, so listed first.
|
80
81
|
select_sql
|
81
82
|
when :first
|
82
83
|
clone(:limit=>1).select_sql
|
@@ -88,6 +89,8 @@ module Sequel
|
|
88
89
|
update_sql(*@prepared_modify_values)
|
89
90
|
when :delete
|
90
91
|
delete_sql
|
92
|
+
else
|
93
|
+
select_sql
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
@@ -122,6 +125,7 @@ module Sequel
|
|
122
125
|
def run(&block)
|
123
126
|
case @prepared_type
|
124
127
|
when :select, :all
|
128
|
+
# Most common scenario, so listed first
|
125
129
|
all(&block)
|
126
130
|
when :insert_select
|
127
131
|
with_sql(prepared_sql).first
|
@@ -133,6 +137,13 @@ module Sequel
|
|
133
137
|
update(*@prepared_modify_values)
|
134
138
|
when :delete
|
135
139
|
delete
|
140
|
+
when Array
|
141
|
+
case @prepared_type.at(0)
|
142
|
+
when :map, :to_hash
|
143
|
+
send(*@prepared_type, &block)
|
144
|
+
end
|
145
|
+
else
|
146
|
+
all(&block)
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -419,14 +419,10 @@ module Sequel
|
|
419
419
|
val_array = true
|
420
420
|
empty_val_array = vals == []
|
421
421
|
end
|
422
|
-
if
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
else
|
427
|
-
literal_append(sql, 1=>1)
|
428
|
-
end
|
429
|
-
elsif !supports_multiple_column_in?
|
422
|
+
if empty_val_array
|
423
|
+
literal_append(sql, empty_array_value(op, cols))
|
424
|
+
elsif col_array
|
425
|
+
if !supports_multiple_column_in?
|
430
426
|
if val_array
|
431
427
|
expr = SQL::BooleanExpression.new(:OR, *vals.to_a.map{|vs| SQL::BooleanExpression.from_value_pairs(cols.to_a.zip(vs).map{|c, v| [c, v]})})
|
432
428
|
literal_append(sql, op == :IN ? expr : ~expr)
|
@@ -452,19 +448,11 @@ module Sequel
|
|
452
448
|
sql << PAREN_CLOSE
|
453
449
|
end
|
454
450
|
else
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
end
|
461
|
-
else
|
462
|
-
sql << PAREN_OPEN
|
463
|
-
literal_append(sql, cols)
|
464
|
-
sql << SPACE << op.to_s << SPACE
|
465
|
-
literal_append(sql, vals)
|
466
|
-
sql << PAREN_CLOSE
|
467
|
-
end
|
451
|
+
sql << PAREN_OPEN
|
452
|
+
literal_append(sql, cols)
|
453
|
+
sql << SPACE << op.to_s << SPACE
|
454
|
+
literal_append(sql, vals)
|
455
|
+
sql << PAREN_CLOSE
|
468
456
|
end
|
469
457
|
when *TWO_ARITY_OPERATORS
|
470
458
|
sql << PAREN_OPEN
|
@@ -856,6 +844,11 @@ module Sequel
|
|
856
844
|
:"#{DATASET_ALIAS_BASE_NAME}#{number}"
|
857
845
|
end
|
858
846
|
|
847
|
+
# The strftime format to use when literalizing the time.
|
848
|
+
def default_timestamp_format
|
849
|
+
requires_sql_standard_datetimes? ? STANDARD_TIMESTAMP_FORMAT : TIMESTAMP_FORMAT
|
850
|
+
end
|
851
|
+
|
859
852
|
# The order of methods to call to build the DELETE SQL statement
|
860
853
|
def delete_clause_methods
|
861
854
|
DELETE_CLAUSE_METHODS
|
@@ -877,9 +870,13 @@ module Sequel
|
|
877
870
|
end
|
878
871
|
end
|
879
872
|
|
880
|
-
|
881
|
-
|
882
|
-
|
873
|
+
def empty_array_value(op, cols)
|
874
|
+
if Sequel.empty_array_handle_nulls
|
875
|
+
c = Array(cols)
|
876
|
+
SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
|
877
|
+
else
|
878
|
+
{1 => ((op == :IN) ? 0 : 1)}
|
879
|
+
end
|
883
880
|
end
|
884
881
|
|
885
882
|
# Format the timestamp based on the default_timestamp_format, with a couple
|
data/lib/sequel/model.rb
CHANGED
@@ -70,7 +70,8 @@ module Sequel
|
|
70
70
|
EMPTY_INSTANCE_VARIABLES = [:@overridable_methods_module, :@db]
|
71
71
|
|
72
72
|
# Boolean settings that can be modified at the global, class, or instance level.
|
73
|
-
BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting,
|
73
|
+
BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting, \
|
74
|
+
:raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_after_commit_rollback, :use_transactions]
|
74
75
|
|
75
76
|
# Hooks that are called before an action. Can return false to not do the action. When
|
76
77
|
# overriding these, it is recommended to call +super+ as the last line of your method,
|
@@ -103,7 +104,8 @@ module Sequel
|
|
103
104
|
:@restricted_columns=>:dup, :@restrict_primary_key=>nil,
|
104
105
|
:@simple_pk=>nil, :@simple_table=>nil, :@strict_param_setting=>nil,
|
105
106
|
:@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
|
106
|
-
:@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil
|
107
|
+
:@raise_on_typecast_failure=>nil, :@plugins=>:dup, :@setter_methods=>nil,
|
108
|
+
:@use_after_commit_rollback=>nil}
|
107
109
|
|
108
110
|
# Regular expression that determines if a method name is normal in the sense that
|
109
111
|
# it could be used literally in ruby code without using send. Used to
|
@@ -133,6 +135,7 @@ module Sequel
|
|
133
135
|
@strict_param_setting = true
|
134
136
|
@typecast_empty_string_to_nil = true
|
135
137
|
@typecast_on_assignment = true
|
138
|
+
@use_after_commit_rollback = true
|
136
139
|
@use_transactions = true
|
137
140
|
|
138
141
|
Sequel.require %w"default_inflections inflections plugins base exceptions errors", "model"
|
@@ -268,6 +268,18 @@ module Sequel
|
|
268
268
|
end
|
269
269
|
alias associated_object_keys primary_keys
|
270
270
|
|
271
|
+
# The method symbol or array of method symbols to call on the associated object
|
272
|
+
# to get the value to use for the foreign keys.
|
273
|
+
def primary_key_method
|
274
|
+
self[:primary_key_method] ||= primary_key
|
275
|
+
end
|
276
|
+
|
277
|
+
# The array of method symbols to call on the associated object
|
278
|
+
# to get the value to use for the foreign keys.
|
279
|
+
def primary_key_methods
|
280
|
+
self[:primary_key_methods] ||= Array(primary_key_method)
|
281
|
+
end
|
282
|
+
|
271
283
|
# #primary_key qualified by the associated table
|
272
284
|
def qualified_primary_key
|
273
285
|
self[:qualified_primary_key] ||= self[:qualify] == false ? primary_key : qualify_assoc(primary_key)
|
@@ -528,6 +540,18 @@ module Sequel
|
|
528
540
|
self[:right_primary_keys] ||= Array(right_primary_key)
|
529
541
|
end
|
530
542
|
|
543
|
+
# The method symbol or array of method symbols to call on the associated objects
|
544
|
+
# to get the foreign key values for the join table.
|
545
|
+
def right_primary_key_method
|
546
|
+
self[:right_primary_key_method] ||= right_primary_key
|
547
|
+
end
|
548
|
+
|
549
|
+
# The array of method symbols to call on the associated objects
|
550
|
+
# to get the foreign key values for the join table.
|
551
|
+
def right_primary_key_methods
|
552
|
+
self[:right_primary_key_methods] ||= Array(right_primary_key_method)
|
553
|
+
end
|
554
|
+
|
531
555
|
# The columns to select when loading the association, associated_class.table_name.* by default.
|
532
556
|
def select
|
533
557
|
return self[:select] if include?(:select)
|
@@ -763,11 +787,13 @@ module Sequel
|
|
763
787
|
# array of symbols for a composite key association.
|
764
788
|
# :key_column :: Similar to, and usually identical to, :key, but :key refers to the model method
|
765
789
|
# to call, where :key_column refers to the underlying column. Should only be
|
766
|
-
# used if the
|
790
|
+
# used if the the model method differs from the foreign key column, in conjunction
|
767
791
|
# with defining a model alias method for the key column.
|
768
792
|
# :primary_key :: column in the associated table that :key option references, as a symbol.
|
769
793
|
# Defaults to the primary key of the associated table. Can use an
|
770
794
|
# array of symbols for a composite key association.
|
795
|
+
# :primary_key_method :: the method symbol or array of method symbols to call on the associated
|
796
|
+
# object to get the foreign key values. Defaults to :primary_key option.
|
771
797
|
# :qualify :: Whether to use qualifier primary keys when loading the association. The default
|
772
798
|
# is true, so you must set to false to not qualify. Qualification rarely causes
|
773
799
|
# problems, but it's necessary to disable in some cases, such as when you are doing
|
@@ -777,9 +803,15 @@ module Sequel
|
|
777
803
|
# current model's primary key, as a symbol. Defaults to
|
778
804
|
# :"#{self.name.underscore}_id". Can use an
|
779
805
|
# array of symbols for a composite key association.
|
806
|
+
# :key_method :: the method symbol or array of method symbols to call on the associated
|
807
|
+
# object to get the foreign key values. Defaults to :key option.
|
780
808
|
# :primary_key :: column in the current table that :key option references, as a symbol.
|
781
809
|
# Defaults to primary key of the current table. Can use an
|
782
810
|
# array of symbols for a composite key association.
|
811
|
+
# :primary_key_column :: Similar to, and usually identical to, :primary_key, but :primary_key refers
|
812
|
+
# to the model method call, where :primary_key_column refers to the underlying column.
|
813
|
+
# Should only be used if the the model method differs from the primary key column, in
|
814
|
+
# conjunction with defining a model alias method for the primary key column.
|
783
815
|
# === :many_to_many
|
784
816
|
# :graph_join_table_block :: The block to pass to +join_table+ for
|
785
817
|
# the join table when eagerly loading the association via +eager_graph+.
|
@@ -806,12 +838,19 @@ module Sequel
|
|
806
838
|
# :left_primary_key :: column in current table that :left_key points to, as a symbol.
|
807
839
|
# Defaults to primary key of current table. Can use an
|
808
840
|
# array of symbols for a composite key association.
|
841
|
+
# :left_primary_key_column :: Similar to, and usually identical to, :left_primary_key, but :left_primary_key refers to
|
842
|
+
# the model method to call, where :left_primary_key_column refers to the underlying column. Should only
|
843
|
+
# be used if the model method differs from the left primary key column, in conjunction
|
844
|
+
# with defining a model alias method for the left primary key column.
|
809
845
|
# :right_key :: foreign key in join table that points to associated
|
810
846
|
# model's primary key, as a symbol. Defaults to Defaults to :"#{name.to_s.singularize}_id".
|
811
847
|
# Can use an array of symbols for a composite key association.
|
812
848
|
# :right_primary_key :: column in associated table that :right_key points to, as a symbol.
|
813
849
|
# Defaults to primary key of the associated table. Can use an
|
814
850
|
# array of symbols for a composite key association.
|
851
|
+
# :right_primary_key_method :: the method symbol or array of method symbols to call on the associated
|
852
|
+
# object to get the foreign key values for the join table.
|
853
|
+
# Defaults to :right_primary_key option.
|
815
854
|
# :uniq :: Adds a after_load callback that makes the array of objects unique.
|
816
855
|
def associate(type, name, opts = {}, &block)
|
817
856
|
raise(Error, 'one_to_many association type with :one_to_one option removed, used one_to_one association type') if opts[:one_to_one] && type == :one_to_many
|
@@ -1034,6 +1073,9 @@ module Sequel
|
|
1034
1073
|
rcks = opts[:right_keys] = Array(right)
|
1035
1074
|
left_pk = (opts[:left_primary_key] ||= self.primary_key)
|
1036
1075
|
lcpks = opts[:left_primary_keys] = Array(left_pk)
|
1076
|
+
lpkc = opts[:left_primary_key_column] ||= left_pk
|
1077
|
+
lpkcs = opts[:left_primary_key_columns] ||= Array(lpkc)
|
1078
|
+
elk = opts.eager_loader_key
|
1037
1079
|
raise(Error, "mismatched number of left composite keys: #{lcks.inspect} vs #{lcpks.inspect}") unless lcks.length == lcpks.length
|
1038
1080
|
if opts[:right_primary_key]
|
1039
1081
|
rcpks = Array(opts[:right_primary_key])
|
@@ -1050,7 +1092,7 @@ module Sequel
|
|
1050
1092
|
opts[:dataset] ||= proc{opts.associated_class.inner_join(join_table, rcks.zip(opts.right_primary_keys) + lcks.zip(lcpks.map{|k| send(k)}))}
|
1051
1093
|
|
1052
1094
|
opts[:eager_loader] ||= proc do |eo|
|
1053
|
-
h = eo[:key_hash][
|
1095
|
+
h = eo[:key_hash][elk]
|
1054
1096
|
rows = eo[:rows]
|
1055
1097
|
rows.each{|object| object.associations[name] = []}
|
1056
1098
|
r = rcks.zip(opts.right_primary_keys)
|
@@ -1095,7 +1137,7 @@ module Sequel
|
|
1095
1137
|
jt_graph_block = opts[:graph_join_table_block]
|
1096
1138
|
opts[:eager_grapher] ||= proc do |eo|
|
1097
1139
|
ds = eo[:self]
|
1098
|
-
ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(
|
1140
|
+
ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lpkcs) + graph_jt_conds, :select=>false, :table_alias=>ds.unused_table_alias(join_table, [eo[:table_alias]]), :join_type=>jt_join_type, :implicit_qualifier=>eo[:implicit_qualifier], :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
|
1099
1141
|
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.right_primary_keys.zip(rcks) + conditions, :select=>select, :table_alias=>eo[:table_alias], :join_type=>join_type, &graph_block)
|
1100
1142
|
end
|
1101
1143
|
|
@@ -1106,11 +1148,11 @@ module Sequel
|
|
1106
1148
|
association_module_private_def(opts._add_method, opts) do |o|
|
1107
1149
|
h = {}
|
1108
1150
|
lcks.zip(lcpks).each{|k, pk| h[k] = send(pk)}
|
1109
|
-
rcks.zip(opts.
|
1151
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.send(pk)}
|
1110
1152
|
_join_table_dataset(opts).insert(h)
|
1111
1153
|
end
|
1112
1154
|
association_module_private_def(opts._remove_method, opts) do |o|
|
1113
|
-
_join_table_dataset(opts).filter(lcks.zip(lcpks.map{|k| send(k)}) + rcks.zip(opts.
|
1155
|
+
_join_table_dataset(opts).filter(lcks.zip(lcpks.map{|k| send(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.send(k)})).delete
|
1114
1156
|
end
|
1115
1157
|
association_module_private_def(opts._remove_all_method, opts) do
|
1116
1158
|
_join_table_dataset(opts).filter(lcks.zip(lcpks.map{|k| send(k)})).delete
|
@@ -1154,7 +1196,7 @@ module Sequel
|
|
1154
1196
|
unless keys.empty?
|
1155
1197
|
klass = opts.associated_class
|
1156
1198
|
model.eager_loading_dataset(opts, klass.filter(opts.qualified_primary_key=>keys), nil, eo[:associations], eo).all do |assoc_record|
|
1157
|
-
hash_key = uses_cks ? opts.
|
1199
|
+
hash_key = uses_cks ? opts.primary_key_methods.map{|k| assoc_record.send(k)} : assoc_record.send(opts.primary_key_method)
|
1158
1200
|
next unless objects = h[hash_key]
|
1159
1201
|
objects.each{|object| object.associations[name] = assoc_record}
|
1160
1202
|
end
|
@@ -1177,7 +1219,7 @@ module Sequel
|
|
1177
1219
|
|
1178
1220
|
return if opts[:read_only]
|
1179
1221
|
|
1180
|
-
association_module_private_def(opts._setter_method, opts){|o| cks.zip(opts.
|
1222
|
+
association_module_private_def(opts._setter_method, opts){|o| cks.zip(opts.primary_key_methods).each{|k, pk| send(:"#{k}=", (o.send(pk) if o))}}
|
1181
1223
|
association_module_def(opts.setter_method, opts){|o| set_associated_object(opts, o)}
|
1182
1224
|
end
|
1183
1225
|
|
@@ -1187,16 +1229,20 @@ module Sequel
|
|
1187
1229
|
name = opts[:name]
|
1188
1230
|
model = self
|
1189
1231
|
key = (opts[:key] ||= opts.default_key)
|
1232
|
+
km = opts[:key_method] ||= opts[:key]
|
1190
1233
|
cks = opts[:keys] = Array(key)
|
1191
1234
|
primary_key = (opts[:primary_key] ||= self.primary_key)
|
1192
1235
|
cpks = opts[:primary_keys] = Array(primary_key)
|
1236
|
+
pkc = opts[:primary_key_column] ||= primary_key
|
1237
|
+
pkcs = opts[:primary_key_columns] ||= Array(pkc)
|
1238
|
+
elk = opts.eager_loader_key
|
1193
1239
|
raise(Error, "mismatched number of composite keys: #{cks.inspect} vs #{cpks.inspect}") unless cks.length == cpks.length
|
1194
1240
|
uses_cks = opts[:uses_composite_keys] = cks.length > 1
|
1195
1241
|
opts[:dataset] ||= proc do
|
1196
1242
|
opts.associated_class.filter(Array(opts.qualified_key).zip(cpks.map{|k| send(k)}))
|
1197
1243
|
end
|
1198
1244
|
opts[:eager_loader] ||= proc do |eo|
|
1199
|
-
h = eo[:key_hash][
|
1245
|
+
h = eo[:key_hash][elk]
|
1200
1246
|
rows = eo[:rows]
|
1201
1247
|
if one_to_one
|
1202
1248
|
rows.each{|object| object.associations[name] = nil}
|
@@ -1221,7 +1267,7 @@ module Sequel
|
|
1221
1267
|
end
|
1222
1268
|
ds.all do |assoc_record|
|
1223
1269
|
assoc_record.values.delete(rn) if delete_rn
|
1224
|
-
hash_key = uses_cks ?
|
1270
|
+
hash_key = uses_cks ? km.map{|k| assoc_record.send(k)} : assoc_record.send(km)
|
1225
1271
|
next unless objects = h[hash_key]
|
1226
1272
|
if one_to_one
|
1227
1273
|
objects.each do |object|
|
@@ -1252,7 +1298,7 @@ module Sequel
|
|
1252
1298
|
graph_block = opts[:graph_block]
|
1253
1299
|
opts[:eager_grapher] ||= proc do |eo|
|
1254
1300
|
ds = eo[:self]
|
1255
|
-
ds = ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : cks.zip(
|
1301
|
+
ds = ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>join_type, :from_self_alias=>ds.opts[:eager_graph][:master]), &graph_block)
|
1256
1302
|
# We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
|
1257
1303
|
ds.opts[:eager_graph][:reciprocals][eo[:table_alias]] = opts.reciprocal
|
1258
1304
|
ds
|
@@ -1939,25 +1985,33 @@ module Sequel
|
|
1939
1985
|
# Return a subquery expression for filering by a many_to_many association
|
1940
1986
|
def many_to_many_association_filter_expression(op, ref, obj)
|
1941
1987
|
lpks, lks, rks = ref.values_at(:left_primary_keys, :left_keys, :right_keys)
|
1988
|
+
jt = ref.join_table_alias
|
1942
1989
|
lpks = lpks.first if lpks.length == 1
|
1943
|
-
|
1990
|
+
lpks = ref.qualify(model.table_name, lpks)
|
1991
|
+
meths = ref.right_primary_keys
|
1992
|
+
meths = ref.qualify(obj.model.table_name, meths) if obj.is_a?(Sequel::Dataset)
|
1993
|
+
exp = association_filter_key_expression(ref.qualify(jt, rks), meths, obj)
|
1944
1994
|
if exp == SQL::Constants::FALSE
|
1945
1995
|
association_filter_handle_inversion(op, exp, Array(lpks))
|
1946
1996
|
else
|
1947
|
-
association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>model.db.from(ref[:join_table]).select(*lks).where(exp).exclude(SQL::BooleanExpression.from_value_pairs(lks.zip([]), :OR))), Array(lpks))
|
1997
|
+
association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>model.db.from(ref[:join_table]).select(*ref.qualify(jt, lks)).where(exp).exclude(SQL::BooleanExpression.from_value_pairs(ref.qualify(jt, lks).zip([]), :OR))), Array(lpks))
|
1948
1998
|
end
|
1949
1999
|
end
|
1950
2000
|
|
1951
2001
|
# Return a simple equality expression for filering by a many_to_one association
|
1952
2002
|
def many_to_one_association_filter_expression(op, ref, obj)
|
1953
|
-
keys = ref[:keys]
|
1954
|
-
|
2003
|
+
keys = ref.qualify(model.table_name, ref[:keys])
|
2004
|
+
meths = ref.primary_keys
|
2005
|
+
meths = ref.qualify(obj.model.table_name, meths) if obj.is_a?(Sequel::Dataset)
|
2006
|
+
association_filter_handle_inversion(op, association_filter_key_expression(keys, meths, obj), keys)
|
1955
2007
|
end
|
1956
2008
|
|
1957
2009
|
# Return a simple equality expression for filering by a one_to_* association
|
1958
2010
|
def one_to_many_association_filter_expression(op, ref, obj)
|
1959
|
-
keys = ref[:primary_keys]
|
1960
|
-
|
2011
|
+
keys = ref.qualify(model.table_name, ref[:primary_keys])
|
2012
|
+
meths = ref[:keys]
|
2013
|
+
meths = ref.qualify(obj.model.table_name, meths) if obj.is_a?(Sequel::Dataset)
|
2014
|
+
association_filter_handle_inversion(op, association_filter_key_expression(keys, meths, obj), keys)
|
1961
2015
|
end
|
1962
2016
|
alias one_to_one_association_filter_expression one_to_many_association_filter_expression
|
1963
2017
|
|