Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/CHANGELOG b/CHANGELOG
- index b735e0f..ee6361b 100644
- --- a/CHANGELOG
- +++ b/CHANGELOG
- @@ -1,5 +1,11 @@
- === HEAD
- +* Add support for filtering and excluding by multiple associations (jeremyevans)
- +
- +* Add support for inverting some SQL::Constant instances such as TRUE, FALSE, NULL, and NOTNULL (jeremyevans)
- +
- +* Add support for excluding by associations to model datasets (jeremyevans)
- +
- * The Sequel::Postgres.use_iso_date_format setting now only affects future Database objects (jeremyevans)
- * Add Sequel::Postgres::PG_NAMED_TYPES hash for extensions to register type conversions for non-standard types (jeremyevans, pvh)
- diff --git a/lib/sequel/model/associations.rb b/lib/sequel/model/associations.rb
- index 84117b6..a0dc083 100644
- --- a/lib/sequel/model/associations.rb
- +++ b/lib/sequel/model/associations.rb
- @@ -1415,14 +1415,21 @@ module Sequel
- # types, this is a simple transformation, but for +many_to_many+ associations this
- # creates a subquery to the join table.
- def complex_expression_sql(op, args)
- - if op == :'=' and args.at(1).is_a?(Sequel::Model)
- - l, r = args
- + r = args.at(1)
- + if (((op == :'=' || op == :'!=') and r.is_a?(Sequel::Model)) ||
- + (multiple = ((op == :IN || op == :'NOT IN') and !r.is_a?(Sequel::Dataset) and r.all?{|x| x.is_a?(Sequel::Model)})))
- + l = args.at(0)
- if ar = model.association_reflections[l]
- - unless r.is_a?(ar.associated_class)
- + if multiple
- + klass = ar.associated_class
- + unless r.all?{|x| x.is_a?(klass)}
- + raise Sequel::Error, "invalid association class for one object for association #{l.inspect} used in dataset filter for model #{model.inspect}, expected class #{klass.inspect}"
- + end
- + elsif !r.is_a?(ar.associated_class)
- raise Sequel::Error, "invalid association class #{r.class.inspect} for association #{l.inspect} used in dataset filter for model #{model.inspect}, expected class #{ar.associated_class.inspect}"
- end
- - if exp = association_filter_expression(ar, r)
- + if exp = association_filter_expression(op, ar, r)
- literal(exp)
- else
- raise Sequel::Error, "invalid association type #{ar[:type].inspect} for association #{l.inspect} used in dataset filter for model #{model.inspect}"
- @@ -1577,9 +1584,42 @@ module Sequel
- private
- # Return an expression for filtering by the given association reflection and associated object.
- - def association_filter_expression(ref, obj)
- + def association_filter_expression(op, ref, obj)
- meth = :"#{ref[:type]}_association_filter_expression"
- - send(meth, ref, obj) if respond_to?(meth, true)
- + send(meth, op, ref, obj) if respond_to?(meth, true)
- + end
- +
- + # Handle inversion for association filters by returning an inverted expression,
- + # plus also handling cases where the referenced columns are NULL.
- + def association_filter_handle_inversion(op, exp, cols)
- + if op == :'!=' || op == :'NOT IN'
- + if exp == SQL::Constants::FALSE
- + ~exp
- + else
- + ~exp | Sequel::SQL::BooleanExpression.from_value_pairs(cols.zip([]), :OR)
- + end
- + else
- + exp
- + end
- + end
- +
- + # Return an expression for making sure that the given keys match the value of
- + # the given methods for either the single object given or for any of the objects
- + # given if +obj+ is an array.
- + def association_filter_key_expression(keys, meths, obj)
- + vals = Array(obj).reject{|o| !meths.all?{|m| o.send(m)}}
- + return SQL::Constants::FALSE if vals.empty?
- + vals = if obj.is_a?(Array)
- + if keys.length == 1
- + meth = meths.first
- + {keys.first=>vals.map{|o| o.send(meth)}}
- + else
- + {keys=>vals.map{|o| meths.map{|m| o.send(m)}}}
- + end
- + else
- + keys.zip(meths.map{|k| obj.send(k)})
- + end
- + SQL::BooleanExpression.from_value_pairs(vals)
- end
- # Make sure the association is valid for this model, and return the related AssociationReflection.
- @@ -1685,20 +1725,27 @@ module Sequel
- end
- # Return a subquery expression for filering by a many_to_many association
- - def many_to_many_association_filter_expression(ref, obj)
- - lpks, lks, rks = ref.values_at(:left_primary_keys, :left_keys, :right_keys)
- - lpks = lpks.first if lpks.length == 1
- - SQL::BooleanExpression.from_value_pairs(lpks=>model.db[ref[:join_table]].select(*lks).where(rks.zip(ref.right_primary_keys.map{|k| obj.send(k)})))
- + def many_to_many_association_filter_expression(op, ref, obj)
- + lpks, lks, rks = ref.values_at(:left_primary_keys, :left_keys, :right_keys)
- + lpks = lpks.first if lpks.length == 1
- + exp = association_filter_key_expression(rks, ref.right_primary_keys, obj)
- + if exp == SQL::Constants::FALSE
- + association_filter_handle_inversion(op, exp, Array(lpks))
- + else
- + association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>model.db[ref[:join_table]].select(*lks).where(exp).exclude(SQL::BooleanExpression.from_value_pairs(lks.zip([]), :OR))), Array(lpks))
- + end
- end
- # Return a simple equality expression for filering by a many_to_one association
- - def many_to_one_association_filter_expression(ref, obj)
- - SQL::BooleanExpression.from_value_pairs(ref[:keys].zip(ref.primary_keys.map{|k| obj.send(k)}))
- + def many_to_one_association_filter_expression(op, ref, obj)
- + keys = ref[:keys]
- + association_filter_handle_inversion(op, association_filter_key_expression(keys, ref.primary_keys, obj), keys)
- end
- # Return a simple equality expression for filering by a one_to_* association
- - def one_to_many_association_filter_expression(ref, obj)
- - SQL::BooleanExpression.from_value_pairs(ref[:primary_keys].zip(ref[:keys].map{|k| obj.send(k)}))
- + def one_to_many_association_filter_expression(op, ref, obj)
- + keys = ref[:primary_keys]
- + association_filter_handle_inversion(op, association_filter_key_expression(keys, ref[:keys], obj), keys)
- end
- alias one_to_one_association_filter_expression one_to_many_association_filter_expression
- diff --git a/lib/sequel/plugins/many_through_many.rb b/lib/sequel/plugins/many_through_many.rb
- index b495051..7f4c34e 100644
- --- a/lib/sequel/plugins/many_through_many.rb
- +++ b/lib/sequel/plugins/many_through_many.rb
- @@ -224,7 +224,7 @@ module Sequel
- private
- # Use a subquery to filter rows to those related to the given associated object
- - def many_through_many_association_filter_expression(ref, obj)
- + def many_through_many_association_filter_expression(op, ref, obj)
- lpks = ref[:left_primary_keys]
- lpks = lpks.first if lpks.length == 1
- edges = ref.edges
- @@ -238,8 +238,8 @@ module Sequel
- last_join = ds.opts[:join].last
- last_join.table_alias || last_join.table
- end
- - ds = ds.where(Array(ref[:final_edge][:left]).map{|x| ::Sequel::SQL::QualifiedIdentifier.new(last_alias, x)}.zip(ref.right_primary_keys.map{|k| obj.send(k)}))
- - SQL::BooleanExpression.from_value_pairs(lpks=>ds)
- + ds = ds.where(Array(ref[:final_edge][:left]).map{|x| ::Sequel::SQL::QualifiedIdentifier.new(last_alias, x)}.zip(ref.right_primary_keys.map{|k| obj.send(k)})).exclude(SQL::BooleanExpression.from_value_pairs(ds.opts[:select].zip([]), :OR))
- + association_filter_handle_inversion(op, SQL::BooleanExpression.from_value_pairs(lpks=>ds), Array(lpks))
- end
- end
- end
- diff --git a/lib/sequel/sql.rb b/lib/sequel/sql.rb
- index 80a7aaa..36f56e5 100644
- --- a/lib/sequel/sql.rb
- +++ b/lib/sequel/sql.rb
- @@ -595,6 +595,8 @@ module Sequel
- end
- when StringExpression, NumericExpression
- raise(Sequel::Error, "cannot invert #{ce.inspect}")
- + when Constant
- + CONSTANT_INVERSIONS[ce] || raise(Sequel::Error, "cannot invert #{ce.inspect}")
- else
- BooleanExpression.new(:NOT, ce)
- end
- @@ -706,6 +708,12 @@ module Sequel
- NOTNULL = NegativeBooleanConstant.new(nil)
- end
- + class ComplexExpression
- + # A hash of the opposite for each constant, used for inverting constants.
- + CONSTANT_INVERSIONS = {Constants::TRUE=>Constants::FALSE, Constants::FALSE=>Constants::TRUE,
- + Constants::NULL=>Constants::NOTNULL, Constants::NOTNULL=>Constants::NULL}
- + end
- +
- # Represents an SQL function call.
- class Function < GenericExpression
- # The array of arguments to pass to the function (may be blank)
- diff --git a/spec/core/expression_filters_spec.rb b/spec/core/expression_filters_spec.rb
- index 492f8f1..3c6de5d 100644
- --- a/spec/core/expression_filters_spec.rb
- +++ b/spec/core/expression_filters_spec.rb
- @@ -424,6 +424,19 @@ describe "Blockless Ruby Filters" do
- @d.l(~{:x => Sequel::SQLFALSE}).should == '(x IS NOT FALSE)'
- end
- + it "should support direct negation of SQL::Constants" do
- + @d.l({:x => ~Sequel::NULL}).should == '(x IS NOT NULL)'
- + @d.l({:x => ~Sequel::NOTNULL}).should == '(x IS NULL)'
- + @d.l({:x => ~Sequel::TRUE}).should == '(x IS FALSE)'
- + @d.l({:x => ~Sequel::FALSE}).should == '(x IS TRUE)'
- + @d.l({:x => ~Sequel::SQLTRUE}).should == '(x IS FALSE)'
- + @d.l({:x => ~Sequel::SQLFALSE}).should == '(x IS TRUE)'
- + end
- +
- + it "should raise an error if trying to invert an invalid SQL::Constant" do
- + proc{~Sequel::CURRENT_DATE}.should raise_error(Sequel::Error)
- + end
- +
- it "should raise an error if trying to create an invalid complex expression" do
- proc{Sequel::SQL::ComplexExpression.new(:BANG, 1, 2)}.should raise_error(Sequel::Error)
- end
- diff --git a/spec/extensions/many_through_many_spec.rb b/spec/extensions/many_through_many_spec.rb
- index e68a8b3..55c9fcb 100644
- --- a/spec/extensions/many_through_many_spec.rb
- +++ b/spec/extensions/many_through_many_spec.rb
- @@ -114,24 +114,35 @@ describe Sequel::Model, "many_through_many" do
- it "should allowing filtering by many_through_many associations" do
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
- - @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE (albums_tags.tag_id = 1234)))'
- + @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
- end
- it "should allowing filtering by many_through_many associations with a single through table" do
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id]]
- - @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE (albums_artists.album_id = 1234)))'
- + @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
- end
- it "should allowing filtering by many_through_many associations with aliased tables" do
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :id, :id], [:albums_artists, :album_id, :tag_id]]
- - @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE (albums_artists_1.tag_id = 1234)))'
- + @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE ((albums_artists_1.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
- end
- it "should allowing filtering by many_through_many associations with composite keys" do
- @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
- - @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85))))'
- + @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
- end
- + it "should allowing excluding by many_through_many associations" do
- + @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
- + @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
- + end
- +
- + it "should allowing excluding by many_through_many associations with composite keys" do
- + @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
- + @c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
- + end
- +
- +
- it "should support a :conditions option" do
- @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
- n = @c1.load(:id => 1234)
- diff --git a/spec/integration/associations_test.rb b/spec/integration/associations_test.rb
- index b2fe47a..f0d6cfa 100644
- --- a/spec/integration/associations_test.rb
- +++ b/spec/integration/associations_test.rb
- @@ -26,10 +26,6 @@ shared_examples_for "regular and composite key associations" do
- @album.update(:artist => @artist)
- @album.add_tag(@tag)
- - @album.reload
- - @artist.reload
- - @tag.reload
- -
- Artist.filter(:albums=>@album).all.should == [@artist]
- Album.filter(:artist=>@artist).all.should == [@album]
- Album.filter(:tags=>@tag).all.should == [@album]
- @@ -37,7 +33,40 @@ shared_examples_for "regular and composite key associations" do
- Album.filter(:artist=>@artist, :tags=>@tag).all.should == [@album]
- @artist.albums_dataset.filter(:tags=>@tag).all.should == [@album]
- end
- +
- + specify "should work correctly when excluding by associations" do
- + @album.update(:artist => @artist)
- + @album.add_tag(@tag)
- + album, artist, tag = @pr.call
- +
- + Artist.exclude(:albums=>@album).all.should == [artist]
- + Album.exclude(:artist=>@artist).all.should == [album]
- + Album.exclude(:tags=>@tag).all.should == [album]
- + Tag.exclude(:albums=>@album).all.should == [tag]
- + Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [album]
- + end
- + specify "should work correctly when excluding by associations in regards to NULL values" do
- + Artist.exclude(:albums=>@album).all.should == [@artist]
- + Album.exclude(:artist=>@artist).all.should == [@album]
- + Album.exclude(:tags=>@tag).all.should == [@album]
- + Tag.exclude(:albums=>@album).all.should == [@tag]
- + Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [@album]
- +
- + @album.update(:artist => @artist)
- + @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
- + end
- +
- + specify "should handle NULL values in join table correctly when filtering/excluding many_to_many associations" do
- + @ins.call
- + Album.exclude(:tags=>@tag).all.should == [@album]
- + @album.add_tag(@tag)
- + Album.filter(:tags=>@tag).all.should == [@album]
- + album, artist, tag = @pr.call
- + Album.exclude(:tags=>@tag).all.should == [album]
- + Album.exclude(:tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
- + end
- +
- specify "should have remove methods work" do
- @album.update(:artist => @artist)
- @album.add_tag(@tag)
- @@ -167,6 +196,8 @@ describe "Sequel::Model Simple Associations" do
- @album = Album.create(:name=>'Al')
- @artist = Artist.create(:name=>'Ar')
- @tag = Tag.create(:name=>'T')
- + @pr = lambda{[Album.create(:name=>'Al2'),Artist.create(:name=>'Ar2'),Tag.create(:name=>'T2')]}
- + @ins = lambda{@db[:albums_tags].insert(:tag_id=>@tag.id)}
- end
- after do
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
- @@ -333,6 +364,8 @@ describe "Sequel::Model Composite Key Associations" do
- @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
- @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
- @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
- + @pr = lambda{[Album.create(:name=>'Al2', :id1=>11, :id2=>12),Artist.create(:name=>'Ar2', :id1=>13, :id2=>14),Tag.create(:name=>'T2', :id1=>15, :id2=>16)]}
- + @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
- end
- after do
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
- diff --git a/spec/integration/plugin_test.rb b/spec/integration/plugin_test.rb
- index b454484..6fbc4e2 100644
- --- a/spec/integration/plugin_test.rb
- +++ b/spec/integration/plugin_test.rb
- @@ -210,6 +210,11 @@ describe "Many Through Many Plugin" do
- Artist.filter(:albums=>@album2).all.map{|a| a.name}.sort.should == %w'3 4'
- Artist.filter(:albums=>@album3).all.map{|a| a.name}.sort.should == %w'2 3'
- Artist.filter(:albums=>@album4).all.map{|a| a.name}.sort.should == %w'1 4'
- +
- + Artist.exclude(:albums=>@album1).all.map{|a| a.name}.sort.should == %w'3 4'
- + Artist.exclude(:albums=>@album2).all.map{|a| a.name}.sort.should == %w'1 2'
- + Artist.exclude(:albums=>@album3).all.map{|a| a.name}.sort.should == %w'1 4'
- + Artist.exclude(:albums=>@album4).all.map{|a| a.name}.sort.should == %w'2 3'
- end
- specify "should handle typical case with 3 join tables" do
- @@ -233,6 +238,11 @@ describe "Many Through Many Plugin" do
- Artist.filter(:related_artists=>@artist2).all.map{|a| a.name}.sort.should == %w'1 2 3'
- Artist.filter(:related_artists=>@artist3).all.map{|a| a.name}.sort.should == %w'2 3 4'
- Artist.filter(:related_artists=>@artist4).all.map{|a| a.name}.sort.should == %w'1 3 4'
- +
- + Artist.exclude(:related_artists=>@artist1).all.map{|a| a.name}.sort.should == %w'3'
- + Artist.exclude(:related_artists=>@artist2).all.map{|a| a.name}.sort.should == %w'4'
- + Artist.exclude(:related_artists=>@artist3).all.map{|a| a.name}.sort.should == %w'1'
- + Artist.exclude(:related_artists=>@artist4).all.map{|a| a.name}.sort.should == %w'2'
- end
- specify "should handle extreme case with 5 join tables" do
- @@ -265,6 +275,11 @@ describe "Many Through Many Plugin" do
- Artist.filter(:related_albums=>@album2).all.map{|a| a.name}.sort.should == %w'1 2 3 4'
- Artist.filter(:related_albums=>@album3).all.map{|a| a.name}.sort.should == %w'1 2'
- Artist.filter(:related_albums=>@album4).all.map{|a| a.name}.sort.should == %w'2 3 4'
- +
- + Artist.exclude(:related_albums=>@album1).all.map{|a| a.name}.sort.should == %w'4'
- + Artist.exclude(:related_albums=>@album2).all.map{|a| a.name}.sort.should == %w''
- + Artist.exclude(:related_albums=>@album3).all.map{|a| a.name}.sort.should == %w'3 4'
- + Artist.exclude(:related_albums=>@album4).all.map{|a| a.name}.sort.should == %w'1'
- end
- end
- diff --git a/spec/model/associations_spec.rb b/spec/model/associations_spec.rb
- index 2f0a601..9e16278 100644
- --- a/spec/model/associations_spec.rb
- +++ b/spec/model/associations_spec.rb
- @@ -2795,7 +2795,7 @@ describe "Filtering by associations" do
- end
- it "should be able to filter on many_to_many associations" do
- - @Album.filter(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE (tag_id = 3)))'
- + @Album.filter(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id = 3) AND (album_id IS NOT NULL))))'
- end
- it "should be able to filter on many_to_one associations with composite keys" do
- @@ -2811,7 +2811,7 @@ describe "Filtering by associations" do
- end
- it "should be able to filter on many_to_many associations with composite keys" do
- - @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE ((tag_id1 = 3) AND (tag_id2 = 4))))'
- + @Album.filter(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE ((tag_id1 = 3) AND (tag_id2 = 4) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
- end
- it "should work inside a complex filter" do
- @@ -2840,4 +2840,261 @@ describe "Filtering by associations" do
- c.many_to_one :sartist, :class=>@Artist
- c.filter(:sartist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (sartist_id = 3)'
- end
- +
- + it "should be able to exclude on many_to_one associations" do
- + @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((artist_id != 3) OR (artist_id IS NULL))'
- + end
- +
- + it "should be able to exclude on one_to_many associations" do
- + @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on one_to_one associations" do
- + @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on many_to_many associations" do
- + @Album.exclude(:tags=>@Tag.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id = 3) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on many_to_one associations with composite keys" do
- + @Album.exclude(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1 != 3) OR (artist_id2 != 4) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on one_to_many associations with composite keys" do
- + @Album.exclude(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 != 3) OR (id2 != 4) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on one_to_one associations with composite keys" do
- + @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 != 3) OR (id2 != 4) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on many_to_many associations with composite keys" do
- + @Album.exclude(:ctags=>@Tag.load(:tid1=>3, :tid2=>4)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE ((tag_id1 = 3) AND (tag_id2 = 4) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to filter on multiple many_to_one associations" do
- + @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3, 4))'
- + end
- +
- + it "should be able to filter on multiple one_to_many associations" do
- + @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
- + end
- +
- + it "should be able to filter on multiple one_to_one associations" do
- + @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
- + end
- +
- + it "should be able to filter on multiple many_to_many associations" do
- + @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3, 4)) AND (album_id IS NOT NULL))))'
- + end
- +
- + it "should be able to filter on multiple many_to_one associations with composite keys" do
- + @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4), (5, 6)))'
- + end
- +
- + it "should be able to filter on multiple one_to_many associations with composite keys" do
- + @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4), (5, 6)))'
- + end
- +
- + it "should be able to filter on multiple one_to_one associations with composite keys" do
- + @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4), (5, 6)))'
- + end
- +
- + it "should be able to filter on multiple many_to_many associations with composite keys" do
- + @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4), (5, 6))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
- + end
- +
- + it "should be able to exclude on multiple many_to_one associations" do
- + @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (3, 4)) OR (artist_id IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple one_to_many associations" do
- + @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3, 4)) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple one_to_one associations" do
- + @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3, 4)) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple many_to_many associations" do
- + @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3, 4)) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple many_to_one associations with composite keys" do
- + @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>5, :id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4), (5, 6))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple one_to_many associations with composite keys" do
- + @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4), (5, 6))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple one_to_one associations with composite keys" do
- + @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5, :album_id2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4), (5, 6))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to exclude on multiple many_to_many associations with composite keys" do
- + @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5, :tid2=>6)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4), (5, 6))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to handle NULL values when filtering many_to_one associations" do
- + @Album.filter(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering one_to_many associations" do
- + @Album.filter(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering one_to_one associations" do
- + @Album.filter(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering many_to_many associations" do
- + @Album.filter(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle filteringing with NULL values for many_to_one associations with composite keys" do
- + @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to filtering with NULL values for one_to_many associations with composite keys" do
- + @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to filtering with NULL values for one_to_one associations with composite keys" do
- + @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to filtering with NULL values for many_to_many associations with composite keys" do
- + @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + @Album.filter(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when excluding many_to_one associations" do
- + @Album.exclude(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding one_to_many associations" do
- + @Album.exclude(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding one_to_one associations" do
- + @Album.exclude(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding many_to_many associations" do
- + @Album.exclude(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
- + @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
- + @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
- + @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
- + @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + @Album.exclude(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when filtering multiple many_to_one associations" do
- + @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3))'
- + @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering multiple one_to_many associations" do
- + @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
- + @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering multiple one_to_one associations" do
- + @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
- + @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering multiple many_to_many associations" do
- + @Album.filter(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3)) AND (album_id IS NOT NULL))))'
- + @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
- + end
- +
- + it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
- + @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4)))'
- + @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4)))'
- + end
- +
- + it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
- + @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
- + @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
- + end
- +
- + it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
- + @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
- + @Album.filter(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
- + end
- +
- + it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
- + @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
- + @Album.filter(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
- + end
- +
- + it "should be able to handle NULL values when excluding multiple many_to_one associations" do
- + @Album.exclude(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (3)) OR (artist_id IS NULL))'
- + @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding multiple one_to_many associations" do
- + @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3)) OR (id IS NULL))'
- + @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding multiple one_to_one associations" do
- + @Album.exclude(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3)) OR (id IS NULL))'
- + @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding multiple many_to_many associations" do
- + @Album.exclude(:tags=>[@Tag.load(:id=>3), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (3)) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
- + @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
- + end
- +
- + it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
- + @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.load(:id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
- + @Album.exclude(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN ((3, 4))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
- + end
- +
- + it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
- + @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.load(:album_id1=>3)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + @Album.exclude(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
- + @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.load(:album_id1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + @Album.exclude(:calbum_info=>[@AlbumInfo.load(:album_id1=>3, :album_id2=>4), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN ((3, 4))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- + it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
- + @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.load(:tid1=>5)]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + @Album.exclude(:ctags=>[@Tag.load(:tid1=>3, :tid2=>4), @Tag.new]).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN ((3, 4))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
- + end
- +
- end
Add Comment
Please, Sign In to add comment