sequel 4.3.0 → 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/README.rdoc +7 -7
- data/Rakefile +2 -2
- data/doc/active_record.rdoc +2 -2
- data/doc/association_basics.rdoc +21 -7
- data/doc/bin_sequel.rdoc +2 -2
- data/doc/cheat_sheet.rdoc +2 -1
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/migration.rdoc +2 -2
- data/doc/object_model.rdoc +2 -2
- data/doc/opening_databases.rdoc +13 -1
- data/doc/querying.rdoc +9 -4
- data/doc/release_notes/4.4.0.txt +92 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +2 -2
- data/doc/sql.rdoc +3 -3
- data/doc/thread_safety.rdoc +1 -1
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/jdbc.rb +85 -19
- data/lib/sequel/adapters/jdbc/db2.rb +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/oracle.rb +41 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
- data/lib/sequel/adapters/sqlanywhere.rb +177 -0
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
- data/lib/sequel/core.rb +4 -4
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +2 -2
- data/lib/sequel/dataset.rb +1 -1
- data/lib/sequel/dataset/actions.rb +2 -0
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/query.rb +37 -16
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +2 -2
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
- data/lib/sequel/extensions/pg_array.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +2 -2
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
- data/lib/sequel/extensions/pg_json.rb +2 -2
- data/lib/sequel/extensions/pg_json_ops.rb +2 -2
- data/lib/sequel/extensions/pg_range.rb +2 -2
- data/lib/sequel/extensions/pg_range_ops.rb +2 -2
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/pg_row_ops.rb +3 -3
- data/lib/sequel/model.rb +1 -1
- data/lib/sequel/model/associations.rb +106 -17
- data/lib/sequel/model/base.rb +23 -19
- data/lib/sequel/plugins/json_serializer.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +14 -6
- data/lib/sequel/plugins/pg_array_associations.rb +28 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/table_select.rb +41 -0
- data/lib/sequel/plugins/tree.rb +1 -1
- data/lib/sequel/sql.rb +2 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/oracle_spec.rb +22 -1
- data/spec/adapters/postgres_spec.rb +31 -48
- data/spec/adapters/sqlanywhere_spec.rb +170 -0
- data/spec/core/dataset_spec.rb +109 -0
- data/spec/core/object_graph_spec.rb +7 -0
- data/spec/extensions/constraint_validations_spec.rb +7 -0
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +65 -0
- data/spec/extensions/pg_array_associations_spec.rb +44 -0
- data/spec/extensions/rcte_tree_spec.rb +3 -3
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/extensions/table_select_spec.rb +71 -0
- data/spec/integration/associations_test.rb +279 -7
- data/spec/integration/dataset_test.rb +13 -4
- data/spec/integration/schema_test.rb +12 -14
- data/spec/model/associations_spec.rb +472 -3
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- data/spec/model/model_spec.rb +10 -0
- metadata +10 -2
@@ -1,12 +1,13 @@
|
|
1
1
|
# The mssql_emulate_lateral_with_apply extension converts
|
2
2
|
# queries that use LATERAL into queries that use CROSS/OUTER
|
3
3
|
# APPLY, allowing code that works on databases that support
|
4
|
-
# LATERAL via Dataset#lateral to run on Microsoft SQL Server
|
4
|
+
# LATERAL via Dataset#lateral to run on Microsoft SQL Server
|
5
|
+
# and Sybase SQLAnywhere.
|
5
6
|
#
|
6
7
|
# This is available as a separate extension instead of
|
7
|
-
# integrated into the Microsoft SQL Server
|
8
|
-
# few people need it and there
|
9
|
-
# code that doesn't use it.
|
8
|
+
# integrated into the Microsoft SQL Server and Sybase
|
9
|
+
# SQLAnywhere support because few people need it and there
|
10
|
+
# is a performance hit to code that doesn't use it.
|
10
11
|
#
|
11
12
|
# It is possible there are cases where this emulation does
|
12
13
|
# not work. Users should probably verify that correct
|
@@ -17,8 +17,8 @@
|
|
17
17
|
#
|
18
18
|
# Sequel.pg_array(array)
|
19
19
|
#
|
20
|
-
# If you have loaded the {core_extensions extension}[
|
21
|
-
# or you have loaded the
|
20
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
21
|
+
# or you have loaded the core_refinements extension
|
22
22
|
# and have activated refinements for the file, you can also use Array#pg_array:
|
23
23
|
#
|
24
24
|
# array.pg_array
|
@@ -19,8 +19,8 @@
|
|
19
19
|
#
|
20
20
|
# ia = Sequel.expr(:int_array_column).pg_array
|
21
21
|
#
|
22
|
-
# If you have loaded the {core_extensions extension}[
|
23
|
-
# or you have loaded the
|
22
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
23
|
+
# or you have loaded the core_refinements extension
|
24
24
|
# and have activated refinements for the file, you can also use Symbol#pg_array:
|
25
25
|
#
|
26
26
|
# ia = :int_array_column.pg_array
|
@@ -19,8 +19,8 @@
|
|
19
19
|
#
|
20
20
|
# Sequel.hstore(hash)
|
21
21
|
#
|
22
|
-
# If you have loaded the {core_extensions extension}[
|
23
|
-
# or you have loaded the
|
22
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
23
|
+
# or you have loaded the core_refinements extension
|
24
24
|
# and have activated refinements for the file, you can also use Hash#hstore:
|
25
25
|
#
|
26
26
|
# hash.hstore
|
@@ -20,8 +20,8 @@
|
|
20
20
|
#
|
21
21
|
# h = Sequel.expr(:hstore_column).hstore
|
22
22
|
#
|
23
|
-
# If you have loaded the {core_extensions extension}[
|
24
|
-
# or you have loaded the
|
23
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
24
|
+
# or you have loaded the core_refinements extension
|
25
25
|
# and have activated refinements for the file, you can also use Symbol#hstore:
|
26
26
|
#
|
27
27
|
# h = :hstore_column.hstore
|
@@ -23,8 +23,8 @@
|
|
23
23
|
# Sequel.pg_json(array)
|
24
24
|
# Sequel.pg_json(hash)
|
25
25
|
#
|
26
|
-
# If you have loaded the {core_extensions extension}[
|
27
|
-
# or you have loaded the
|
26
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
27
|
+
# or you have loaded the core_refinements extension
|
28
28
|
# and have activated refinements for the file, you can also use Array#pg_json and Hash#pg_json:
|
29
29
|
#
|
30
30
|
# array.pg_json
|
@@ -20,8 +20,8 @@
|
|
20
20
|
#
|
21
21
|
# j = Sequel.expr(:json_column).pg_json
|
22
22
|
#
|
23
|
-
# If you have loaded the {core_extensions extension}[
|
24
|
-
# or you have loaded the
|
23
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
24
|
+
# or you have loaded the core_refinements extension
|
25
25
|
# and have activated refinements for the file, you can also use Symbol#pg_json:
|
26
26
|
#
|
27
27
|
# j = :json_column.pg_json
|
@@ -24,8 +24,8 @@
|
|
24
24
|
#
|
25
25
|
# Sequel.pg_range(range)
|
26
26
|
#
|
27
|
-
# If you have loaded the {core_extensions extension}[
|
28
|
-
# or you have loaded the
|
27
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
28
|
+
# or you have loaded the core_refinements extension
|
29
29
|
# and have activated refinements for the file, you can also use Range#pg_range:
|
30
30
|
#
|
31
31
|
# range.pg_range
|
@@ -19,8 +19,8 @@
|
|
19
19
|
#
|
20
20
|
# r = Sequel.expr(:range).pg_range
|
21
21
|
#
|
22
|
-
# If you have loaded the {core_extensions extension}[
|
23
|
-
# or you have loaded the
|
22
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
23
|
+
# or you have loaded the core_refinements extension
|
24
24
|
# and have activated refinements for the file, you can also use Symbol#pg_range:
|
25
25
|
#
|
26
26
|
# r = :range.pg_range
|
@@ -27,8 +27,8 @@
|
|
27
27
|
#
|
28
28
|
# Sequel.pg_row(array)
|
29
29
|
#
|
30
|
-
# If you have loaded the {core_extensions extension}[
|
31
|
-
# or you have loaded the
|
30
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
31
|
+
# or you have loaded the core_refinements extension
|
32
32
|
# and have activated refinements for the file, you can also use Array#pg_row:
|
33
33
|
#
|
34
34
|
# array.pg_row
|
@@ -19,8 +19,8 @@
|
|
19
19
|
#
|
20
20
|
# r = Sequel.expr(:row_column).pg_row
|
21
21
|
#
|
22
|
-
# If you have loaded the {core_extensions extension}[
|
23
|
-
# or you have loaded the
|
22
|
+
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
23
|
+
# or you have loaded the core_refinements extension
|
24
24
|
# and have activated refinements for the file, you can also use Symbol#pg_row:
|
25
25
|
#
|
26
26
|
# r = :row_column.pg_row
|
@@ -70,7 +70,7 @@
|
|
70
70
|
#
|
71
71
|
# DB[:a].select(a.splat).first # SELECT (a.*)::a FROM a
|
72
72
|
# # => {:a=>"(1,2)"} # or {:a=>{:a=>1, :b=>2}} if the "a" type has been registered
|
73
|
-
# # with the
|
73
|
+
# # with the pg_row extension
|
74
74
|
#
|
75
75
|
# This feature is mostly useful for a different way to graph tables:
|
76
76
|
#
|
data/lib/sequel/model.rb
CHANGED
@@ -104,7 +104,7 @@ module Sequel
|
|
104
104
|
# Empty instance methods to create that the user can override to get hook/callback behavior.
|
105
105
|
# Just like any other method defined by Sequel, if you override one of these, you should
|
106
106
|
# call +super+ to get the default behavior (while empty by default, they can also be defined
|
107
|
-
# by plugins). See the {"Model Hooks" guide}[
|
107
|
+
# by plugins). See the {"Model Hooks" guide}[rdoc-ref:doc/model_hooks.rdoc] for
|
108
108
|
# more detail on hooks.
|
109
109
|
HOOKS = BEFORE_HOOKS + AFTER_HOOKS
|
110
110
|
|
@@ -137,6 +137,21 @@ module Sequel
|
|
137
137
|
true
|
138
138
|
end
|
139
139
|
|
140
|
+
# Whether additional conditions should be added when using the filter
|
141
|
+
# by associations support.
|
142
|
+
def filter_by_associations_add_conditions?
|
143
|
+
self[:conditions] || self[:eager_block]
|
144
|
+
end
|
145
|
+
|
146
|
+
# The expression to use for the additional conditions to be added for
|
147
|
+
# the filter by association support, when the association itself is
|
148
|
+
# filtered. Works by using a subquery to test that the objects passed
|
149
|
+
# also meet the association filter criteria.
|
150
|
+
def filter_by_associations_conditions_expression(obj)
|
151
|
+
ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
|
152
|
+
{filter_by_associations_conditions_key=>ds}
|
153
|
+
end
|
154
|
+
|
140
155
|
# The limit and offset for this association (returned as a two element array).
|
141
156
|
def limit_and_offset
|
142
157
|
if (v = self[:limit]).is_a?(Array)
|
@@ -298,6 +313,35 @@ module Sequel
|
|
298
313
|
end
|
299
314
|
end
|
300
315
|
|
316
|
+
# The conditions to add to the filter by associations conditions
|
317
|
+
# subquery to restrict it to to the object(s) that was used as the
|
318
|
+
# filter value.
|
319
|
+
def filter_by_associations_conditions_subquery_conditions(obj)
|
320
|
+
key = qualify(associated_class.table_name, associated_class.primary_key)
|
321
|
+
case obj
|
322
|
+
when Array
|
323
|
+
{key=>obj.map{|o| o.pk}}
|
324
|
+
when Sequel::Dataset
|
325
|
+
{key=>obj.select(*Array(qualify(associated_class.table_name, associated_class.primary_key)))}
|
326
|
+
else
|
327
|
+
Array(key).zip(Array(obj.pk))
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# The base dataset to use for the filter by associations conditions
|
332
|
+
# subquery, regardless of the objects that are passed in as filter
|
333
|
+
# values.
|
334
|
+
def filter_by_associations_conditions_dataset
|
335
|
+
cached_fetch(:filter_by_associations_conditions_dataset) do
|
336
|
+
ds = associated_dataset.unordered.unlimited
|
337
|
+
ds = filter_by_associations_add_conditions_dataset_filter(ds)
|
338
|
+
ds = self[:eager_block].call(ds) if self[:eager_block]
|
339
|
+
ds
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# Whether the given association reflection is possible reciprocal
|
344
|
+
# association for the current association reflection.
|
301
345
|
def reciprocal_association?(assoc_reflect)
|
302
346
|
Array(reciprocal_type).include?(assoc_reflect[:type]) &&
|
303
347
|
assoc_reflect.associated_class == self[:model] &&
|
@@ -395,6 +439,15 @@ module Sequel
|
|
395
439
|
|
396
440
|
private
|
397
441
|
|
442
|
+
def filter_by_associations_add_conditions_dataset_filter(ds)
|
443
|
+
pk = qualify(associated_class.table_name, primary_keys)
|
444
|
+
ds.select(*pk).where(Sequel.negate(pk.zip([])))
|
445
|
+
end
|
446
|
+
|
447
|
+
def filter_by_associations_conditions_key
|
448
|
+
qualify(self[:model].table_name, self[:key_column])
|
449
|
+
end
|
450
|
+
|
398
451
|
def reciprocal_association?(assoc_reflect)
|
399
452
|
super && self[:keys] == assoc_reflect[:keys] && primary_key == assoc_reflect.primary_key
|
400
453
|
end
|
@@ -465,6 +518,15 @@ module Sequel
|
|
465
518
|
|
466
519
|
private
|
467
520
|
|
521
|
+
def filter_by_associations_add_conditions_dataset_filter(ds)
|
522
|
+
k = qualify(associated_class.table_name, self[:keys])
|
523
|
+
ds.select(*k).where(Sequel.negate(k.zip([])))
|
524
|
+
end
|
525
|
+
|
526
|
+
def filter_by_associations_conditions_key
|
527
|
+
qualify(self[:model].table_name, self[:primary_key_column])
|
528
|
+
end
|
529
|
+
|
468
530
|
def reciprocal_association?(assoc_reflect)
|
469
531
|
super && self[:keys] == assoc_reflect[:keys] && primary_key == assoc_reflect.primary_key
|
470
532
|
end
|
@@ -636,6 +698,15 @@ module Sequel
|
|
636
698
|
|
637
699
|
private
|
638
700
|
|
701
|
+
def filter_by_associations_add_conditions_dataset_filter(ds)
|
702
|
+
ds.select(*qualify(join_table_alias, self[:left_keys])).
|
703
|
+
inner_join(self[:join_table], Array(self[:right_keys]).zip(right_primary_keys), :qualify=>:deep)
|
704
|
+
end
|
705
|
+
|
706
|
+
def filter_by_associations_conditions_key
|
707
|
+
qualify(self[:model].table_name, self[:left_primary_key_column])
|
708
|
+
end
|
709
|
+
|
639
710
|
def reciprocal_association?(assoc_reflect)
|
640
711
|
super && assoc_reflect[:left_keys] == self[:right_keys] &&
|
641
712
|
assoc_reflect[:right_keys] == self[:left_keys] &&
|
@@ -710,8 +781,8 @@ module Sequel
|
|
710
781
|
# as a column, you will probably end up with an association that doesn't work, or a SystemStackError.
|
711
782
|
#
|
712
783
|
# For a more in depth general overview, as well as a reference guide,
|
713
|
-
# see the {Association Basics guide}[
|
714
|
-
# For examples of advanced usage, see the {Advanced Associations guide}[
|
784
|
+
# see the {Association Basics guide}[rdoc-ref:doc/association_basics.rdoc].
|
785
|
+
# For examples of advanced usage, see the {Advanced Associations guide}[rdoc-ref:doc/advanced_associations.rdoc].
|
715
786
|
module ClassMethods
|
716
787
|
# All association reflections defined for this model (default: {}).
|
717
788
|
attr_reader :association_reflections
|
@@ -879,7 +950,7 @@ module Sequel
|
|
879
950
|
# array of symbols for a composite key association.
|
880
951
|
# :key_column :: Similar to, and usually identical to, :key, but :key refers to the model method
|
881
952
|
# to call, where :key_column refers to the underlying column. Should only be
|
882
|
-
# used if the
|
953
|
+
# used if the model method differs from the foreign key column, in conjunction
|
883
954
|
# with defining a model alias method for the key column.
|
884
955
|
# :primary_key :: column in the associated table that :key option references, as a symbol.
|
885
956
|
# Defaults to the primary key of the associated table. Can use an
|
@@ -902,7 +973,7 @@ module Sequel
|
|
902
973
|
# array of symbols for a composite key association.
|
903
974
|
# :primary_key_column :: Similar to, and usually identical to, :primary_key, but :primary_key refers
|
904
975
|
# to the model method call, where :primary_key_column refers to the underlying column.
|
905
|
-
# Should only be used if the
|
976
|
+
# Should only be used if the model method differs from the primary key column, in
|
906
977
|
# conjunction with defining a model alias method for the primary key column.
|
907
978
|
# === :many_to_many
|
908
979
|
# :graph_join_table_block :: The block to pass to +join_table+ for
|
@@ -958,7 +1029,7 @@ module Sequel
|
|
958
1029
|
opts = orig_opts.merge(:type => type, :name => name, :cache=>{}, :model => self)
|
959
1030
|
opts[:block] = block if block
|
960
1031
|
opts = assoc_class.new.merge!(opts)
|
961
|
-
opts[:eager_block] = block unless opts.include?(:eager_block)
|
1032
|
+
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
962
1033
|
if !opts.has_key?(:predicate_key) && opts.has_key?(:eager_loading_predicate_key)
|
963
1034
|
opts[:predicate_key] = opts[:eager_loading_predicate_key]
|
964
1035
|
end
|
@@ -980,7 +1051,7 @@ module Sequel
|
|
980
1051
|
send(:"def_#{type}", opts)
|
981
1052
|
|
982
1053
|
orig_opts.delete(:clone)
|
983
|
-
orig_opts.merge!(:class_name=>opts[:class_name], :class=>opts[:class], :block=>block)
|
1054
|
+
orig_opts.merge!(:class_name=>opts[:class_name], :class=>opts[:class], :block=>opts[:block])
|
984
1055
|
opts[:orig_opts] = orig_opts
|
985
1056
|
# don't add to association_reflections until we are sure there are no errors
|
986
1057
|
association_reflections[name] = opts
|
@@ -1516,7 +1587,7 @@ module Sequel
|
|
1516
1587
|
elsif !o.is_a?(klass)
|
1517
1588
|
raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
|
1518
1589
|
end
|
1519
|
-
raise(Sequel::Error, "model object #{inspect} does not have a primary key")
|
1590
|
+
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
1520
1591
|
ensure_associated_primary_key(opts, o, *args)
|
1521
1592
|
return if run_association_callbacks(opts, :before_add, o) == false
|
1522
1593
|
send(opts._add_method, o, *args)
|
@@ -1608,7 +1679,7 @@ module Sequel
|
|
1608
1679
|
|
1609
1680
|
# Remove all associated objects from the given association
|
1610
1681
|
def remove_all_associated_objects(opts, *args)
|
1611
|
-
raise(Sequel::Error, "model object #{inspect} does not have a primary key")
|
1682
|
+
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
1612
1683
|
send(opts._remove_all_method, *args)
|
1613
1684
|
ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
|
1614
1685
|
associations[opts[:name]] = []
|
@@ -1625,7 +1696,7 @@ module Sequel
|
|
1625
1696
|
elsif opts.remove_should_check_existing? && send(opts.dataset_method).where(o.pk_hash).empty?
|
1626
1697
|
raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
|
1627
1698
|
end
|
1628
|
-
raise(Sequel::Error, "model object #{inspect} does not have a primary key")
|
1699
|
+
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
1629
1700
|
raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
|
1630
1701
|
return if run_association_callbacks(opts, :before_remove, o) == false
|
1631
1702
|
send(opts._remove_method, o, *args)
|
@@ -1978,7 +2049,18 @@ module Sequel
|
|
1978
2049
|
end
|
1979
2050
|
|
1980
2051
|
private
|
1981
|
-
|
2052
|
+
|
2053
|
+
# If the association has conditions itself, then it requires additional filters be
|
2054
|
+
# added to the current dataset to ensure that the passed in object would also be
|
2055
|
+
# included by the association's conditions.
|
2056
|
+
def add_association_filter_conditions(ref, obj, expr)
|
2057
|
+
if expr != SQL::Constants::FALSE && ref.filter_by_associations_add_conditions?
|
2058
|
+
Sequel.&(expr, ref.filter_by_associations_conditions_expression(obj))
|
2059
|
+
else
|
2060
|
+
expr
|
2061
|
+
end
|
2062
|
+
end
|
2063
|
+
|
1982
2064
|
# Return an expression for filtering by the given association reflection and associated object.
|
1983
2065
|
def association_filter_expression(op, ref, obj)
|
1984
2066
|
meth = :"#{ref[:type]}_association_filter_expression"
|
@@ -2105,12 +2187,13 @@ module Sequel
|
|
2105
2187
|
ref.right_primary_key_methods
|
2106
2188
|
end
|
2107
2189
|
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
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))
|
2190
|
+
expr = association_filter_key_expression(ref.qualify(jt, rks), meths, obj)
|
2191
|
+
unless expr == SQL::Constants::FALSE
|
2192
|
+
expr = SQL::BooleanExpression.from_value_pairs(lpks=>model.db.from(ref[:join_table]).select(*ref.qualify(jt, lks)).where(expr).exclude(SQL::BooleanExpression.from_value_pairs(ref.qualify(jt, lks).zip([]), :OR)))
|
2193
|
+
expr = add_association_filter_conditions(ref, obj, expr)
|
2113
2194
|
end
|
2195
|
+
|
2196
|
+
association_filter_handle_inversion(op, expr, Array(lpks))
|
2114
2197
|
end
|
2115
2198
|
|
2116
2199
|
# Return a simple equality expression for filering by a many_to_one association
|
@@ -2121,7 +2204,10 @@ module Sequel
|
|
2121
2204
|
else
|
2122
2205
|
ref.primary_key_methods
|
2123
2206
|
end
|
2124
|
-
|
2207
|
+
|
2208
|
+
expr = association_filter_key_expression(keys, meths, obj)
|
2209
|
+
expr = add_association_filter_conditions(ref, obj, expr)
|
2210
|
+
association_filter_handle_inversion(op, expr, keys)
|
2125
2211
|
end
|
2126
2212
|
|
2127
2213
|
# Return a simple equality expression for filering by a one_to_* association
|
@@ -2132,7 +2218,10 @@ module Sequel
|
|
2132
2218
|
else
|
2133
2219
|
ref[:key_methods]
|
2134
2220
|
end
|
2135
|
-
|
2221
|
+
|
2222
|
+
expr = association_filter_key_expression(keys, meths, obj)
|
2223
|
+
expr = add_association_filter_conditions(ref, obj, expr)
|
2224
|
+
association_filter_handle_inversion(op, expr, keys)
|
2136
2225
|
end
|
2137
2226
|
alias one_to_one_association_filter_expression one_to_many_association_filter_expression
|
2138
2227
|
|
data/lib/sequel/model/base.rb
CHANGED
@@ -512,22 +512,7 @@ module Sequel
|
|
512
512
|
# sharding support.
|
513
513
|
def set_dataset(ds, opts=OPTS)
|
514
514
|
inherited = opts[:inherited]
|
515
|
-
|
516
|
-
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
517
|
-
self.simple_table = db.literal(ds)
|
518
|
-
ds = db.from(ds)
|
519
|
-
when Dataset
|
520
|
-
self.simple_table = if ds.send(:simple_select_all?)
|
521
|
-
ds.literal(ds.first_source_table)
|
522
|
-
else
|
523
|
-
nil
|
524
|
-
end
|
525
|
-
@db = ds.db
|
526
|
-
else
|
527
|
-
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
528
|
-
end
|
529
|
-
set_dataset_row_proc(ds)
|
530
|
-
@dataset = ds
|
515
|
+
@dataset = convert_input_dataset(ds)
|
531
516
|
@require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
|
532
517
|
if inherited
|
533
518
|
self.simple_table = superclass.simple_table
|
@@ -644,6 +629,25 @@ module Sequel
|
|
644
629
|
end
|
645
630
|
end
|
646
631
|
|
632
|
+
# Convert the given object to a Dataset that should be used as
|
633
|
+
# this model's dataset.
|
634
|
+
def convert_input_dataset(ds)
|
635
|
+
case ds
|
636
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
|
637
|
+
self.simple_table = db.literal(ds)
|
638
|
+
ds = db.from(ds)
|
639
|
+
when Dataset
|
640
|
+
self.simple_table = if ds.send(:simple_select_all?)
|
641
|
+
ds.literal(ds.first_source_table)
|
642
|
+
end
|
643
|
+
@db = ds.db
|
644
|
+
else
|
645
|
+
raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
|
646
|
+
end
|
647
|
+
set_dataset_row_proc(ds)
|
648
|
+
ds
|
649
|
+
end
|
650
|
+
|
647
651
|
# Add the module to the class's dataset_method_modules. Extend the dataset with the
|
648
652
|
# module if the model has a dataset. Add dataset methods to the class for all
|
649
653
|
# public dataset methods.
|
@@ -703,7 +707,7 @@ module Sequel
|
|
703
707
|
schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
|
704
708
|
if schema_array
|
705
709
|
schema_array.each{|k,v| schema_hash[k] = v}
|
706
|
-
if ds_opts.
|
710
|
+
if (select = ds_opts[:select]) && !(select.length == 1 && select.first.is_a?(SQL::ColumnAll))
|
707
711
|
# We don't remove the columns from the schema_hash,
|
708
712
|
# as it's possible they will be used for typecasting
|
709
713
|
# even if they are not selected.
|
@@ -1022,7 +1026,7 @@ module Sequel
|
|
1022
1026
|
|
1023
1027
|
# Like delete but runs hooks before and after delete.
|
1024
1028
|
# If before_destroy returns false, returns false without
|
1025
|
-
# deleting the object
|
1029
|
+
# deleting the object from the database. Otherwise, deletes
|
1026
1030
|
# the item from the database and returns self. Uses a transaction
|
1027
1031
|
# if use_transactions is true or if the :transaction option is given and
|
1028
1032
|
# true.
|
@@ -1473,7 +1477,7 @@ module Sequel
|
|
1473
1477
|
|
1474
1478
|
# Validates the object. If the object is invalid, errors should be added
|
1475
1479
|
# to the errors attribute. By default, does nothing, as all models
|
1476
|
-
# are valid by default. See the {"Model Validations" guide}[
|
1480
|
+
# are valid by default. See the {"Model Validations" guide}[rdoc-ref:doc/validations.rdoc].
|
1477
1481
|
# for details about validation. Should not be called directly by
|
1478
1482
|
# user code, call <tt>valid?</tt> instead to check if an object
|
1479
1483
|
# is valid.
|