sequel 3.31.0 → 3.32.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.
- 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
|
|