sequel 4.3.0 → 4.4.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.
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.