sequel 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +7 -7
  4. data/Rakefile +2 -2
  5. data/doc/active_record.rdoc +2 -2
  6. data/doc/association_basics.rdoc +21 -7
  7. data/doc/bin_sequel.rdoc +2 -2
  8. data/doc/cheat_sheet.rdoc +2 -1
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/dataset_filtering.rdoc +1 -1
  11. data/doc/migration.rdoc +2 -2
  12. data/doc/object_model.rdoc +2 -2
  13. data/doc/opening_databases.rdoc +13 -1
  14. data/doc/querying.rdoc +9 -4
  15. data/doc/release_notes/4.4.0.txt +92 -0
  16. data/doc/schema_modification.rdoc +1 -1
  17. data/doc/security.rdoc +2 -2
  18. data/doc/sql.rdoc +3 -3
  19. data/doc/thread_safety.rdoc +1 -1
  20. data/doc/validations.rdoc +1 -1
  21. data/doc/virtual_rows.rdoc +1 -1
  22. data/lib/sequel/adapters/jdbc.rb +85 -19
  23. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  24. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  25. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  26. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  27. data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  29. data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
  30. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
  31. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  32. data/lib/sequel/adapters/oracle.rb +1 -1
  33. data/lib/sequel/adapters/shared/db2.rb +5 -0
  34. data/lib/sequel/adapters/shared/oracle.rb +41 -4
  35. data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
  36. data/lib/sequel/adapters/sqlanywhere.rb +177 -0
  37. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
  38. data/lib/sequel/core.rb +4 -4
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/database/schema_generator.rb +1 -1
  42. data/lib/sequel/database/schema_methods.rb +2 -2
  43. data/lib/sequel/dataset.rb +1 -1
  44. data/lib/sequel/dataset/actions.rb +2 -0
  45. data/lib/sequel/dataset/graph.rb +1 -1
  46. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  47. data/lib/sequel/dataset/query.rb +37 -16
  48. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  49. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  50. data/lib/sequel/extensions/migration.rb +1 -1
  51. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
  52. data/lib/sequel/extensions/pg_array.rb +2 -2
  53. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  54. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  55. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
  56. data/lib/sequel/extensions/pg_json.rb +2 -2
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -2
  58. data/lib/sequel/extensions/pg_range.rb +2 -2
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -2
  60. data/lib/sequel/extensions/pg_row.rb +2 -2
  61. data/lib/sequel/extensions/pg_row_ops.rb +3 -3
  62. data/lib/sequel/model.rb +1 -1
  63. data/lib/sequel/model/associations.rb +106 -17
  64. data/lib/sequel/model/base.rb +23 -19
  65. data/lib/sequel/plugins/json_serializer.rb +1 -1
  66. data/lib/sequel/plugins/many_through_many.rb +14 -6
  67. data/lib/sequel/plugins/pg_array_associations.rb +28 -0
  68. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  69. data/lib/sequel/plugins/serialization.rb +11 -0
  70. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  71. data/lib/sequel/plugins/table_select.rb +41 -0
  72. data/lib/sequel/plugins/tree.rb +1 -1
  73. data/lib/sequel/sql.rb +2 -2
  74. data/lib/sequel/version.rb +1 -1
  75. data/spec/adapters/oracle_spec.rb +22 -1
  76. data/spec/adapters/postgres_spec.rb +31 -48
  77. data/spec/adapters/sqlanywhere_spec.rb +170 -0
  78. data/spec/core/dataset_spec.rb +109 -0
  79. data/spec/core/object_graph_spec.rb +7 -0
  80. data/spec/extensions/constraint_validations_spec.rb +7 -0
  81. data/spec/extensions/core_refinements_spec.rb +1 -1
  82. data/spec/extensions/many_through_many_spec.rb +65 -0
  83. data/spec/extensions/pg_array_associations_spec.rb +44 -0
  84. data/spec/extensions/rcte_tree_spec.rb +3 -3
  85. data/spec/extensions/spec_helper.rb +1 -1
  86. data/spec/extensions/table_select_spec.rb +71 -0
  87. data/spec/integration/associations_test.rb +279 -7
  88. data/spec/integration/dataset_test.rb +13 -4
  89. data/spec/integration/schema_test.rb +12 -14
  90. data/spec/model/associations_spec.rb +472 -3
  91. data/spec/model/class_dataset_methods_spec.rb +1 -0
  92. data/spec/model/model_spec.rb +10 -0
  93. 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 support because
8
- # few people need it and there is a performance hit to
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}[link:files/doc/core_extensions_rdoc.html],
21
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html]
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}[link:files/doc/core_extensions_rdoc.html]),
23
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
23
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
24
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
27
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
24
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
28
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
23
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
31
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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}[link:files/doc/core_extensions_rdoc.html]),
23
- # or you have loaded the {core_refinements extension}[link:files/doc/core_refinements_rdoc.html])
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 the pg_row extension
73
+ # # with the pg_row extension
74
74
  #
75
75
  # This feature is mostly useful for a different way to graph tables:
76
76
  #
@@ -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}[link:files/doc/model_hooks_rdoc.html] for
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}[link:files/doc/association_basics_rdoc.html].
714
- # For examples of advanced usage, see the {Advanced Associations guide}[link:files/doc/advanced_associations_rdoc.html].
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 the model method differs from the foreign key column, in conjunction
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 the model method differs from the primary key column, in
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") unless pk
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") unless pk
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") unless pk
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
- exp = association_filter_key_expression(ref.qualify(jt, rks), meths, obj)
2109
- if exp == SQL::Constants::FALSE
2110
- association_filter_handle_inversion(op, exp, Array(lpks))
2111
- else
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
- association_filter_handle_inversion(op, association_filter_key_expression(keys, meths, obj), keys)
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
- association_filter_handle_inversion(op, association_filter_key_expression(keys, meths, obj), keys)
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
 
@@ -512,22 +512,7 @@ module Sequel
512
512
  # sharding support.
513
513
  def set_dataset(ds, opts=OPTS)
514
514
  inherited = opts[:inherited]
515
- case ds
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.include?(:select)
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 the the database. Otherwise, deletes
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}[link:files/doc/validations_rdoc.html].
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.