sequel 3.21.0 → 3.28.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 (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -357,6 +357,28 @@ describe Sequel::Model, "many_to_one" do
357
357
  MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE (nodes.id = 234) LIMIT 1"]
358
358
  end
359
359
 
360
+ it "should use a callback if given one as the argument" do
361
+ @c2.many_to_one :parent, :class => @c2
362
+
363
+ d = @c2.create(:id => 1)
364
+ MODEL_DB.reset
365
+ d.parent_id = 234
366
+ d.associations[:parent] = 42
367
+ d.parent(proc{|ds| ds.filter{name > 'M'}}).should_not == 42
368
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
369
+ end
370
+
371
+ it "should use a block given to the association method as a callback on ruby 1.8.7+" do
372
+ @c2.many_to_one :parent, :class => @c2
373
+
374
+ d = @c2.create(:id => 1)
375
+ MODEL_DB.reset
376
+ d.parent_id = 234
377
+ d.associations[:parent] = 42
378
+ d.parent{|ds| ds.filter{name > 'M'}}.should_not == 42
379
+ MODEL_DB.sqls.should == ["SELECT * FROM nodes WHERE ((nodes.id = 234) AND (name > 'M')) LIMIT 1"]
380
+ end if RUBY_VERSION >= '1.8.7'
381
+
360
382
  it "should have the setter add to the reciprocal one_to_many cached association list if it exists" do
361
383
  @c2.many_to_one :parent, :class => @c2
362
384
  @c2.one_to_many :children, :class => @c2, :key=>:parent_id
@@ -538,6 +560,19 @@ describe Sequel::Model, "many_to_one" do
538
560
  parent.pk.should == 20
539
561
  end
540
562
 
563
+ it "should support after_load association callback that changes the cached object" do
564
+ h = []
565
+ @c2.many_to_one :parent, :class => @c2, :after_load=>:al
566
+ @c2.class_eval do
567
+ def al(v)
568
+ associations[:parent] = :foo
569
+ end
570
+ end
571
+ p = @c2.load(:id=>10, :parent_id=>20)
572
+ p.parent.should == :foo
573
+ p.associations[:parent].should == :foo
574
+ end
575
+
541
576
  it "should raise error and not call internal add or remove method if before callback returns false, even if raise_on_save_failure is false" do
542
577
  # The reason for this is that assignment in ruby always returns the argument instead of the result
543
578
  # of the method, so we can't return nil to signal that the association callback prevented the modification
@@ -1149,6 +1184,16 @@ describe Sequel::Model, "one_to_many" do
1149
1184
  v.model.should == Historical::Value
1150
1185
  end
1151
1186
 
1187
+ it "should use a callback if given one as the argument" do
1188
+ @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1189
+
1190
+ d = @c2.create(:id => 1234)
1191
+ MODEL_DB.reset
1192
+ d.associations[:attributes] = []
1193
+ d.attributes(proc{|ds| ds.filter{name > 'M'}}).should_not == []
1194
+ MODEL_DB.sqls.should == ["SELECT * FROM attributes WHERE ((attributes.nodeid = 1234) AND (name > 'M'))"]
1195
+ end
1196
+
1152
1197
  it "should use explicit key if given" do
1153
1198
  @c2.one_to_many :attributes, :class => @c1, :key => :nodeid
1154
1199
 
@@ -1338,7 +1383,7 @@ describe Sequel::Model, "one_to_many" do
1338
1383
  @c2.one_to_many :attributes, :class => @c1
1339
1384
  n = @c2.new(:id => 1234)
1340
1385
  a = @c1.new(:id => 2345)
1341
- def a.valid?(opts); false; end
1386
+ def a.validate() errors.add(:id, 'foo') end
1342
1387
  proc{n.add_attribute(a)}.should raise_error(Sequel::Error)
1343
1388
  proc{n.remove_attribute(a)}.should raise_error(Sequel::Error)
1344
1389
  end
@@ -1347,7 +1392,7 @@ describe Sequel::Model, "one_to_many" do
1347
1392
  @c2.one_to_many :attributes, :class => @c1, :validate=>false
1348
1393
  n = @c2.new(:id => 1234)
1349
1394
  a = @c1.new(:id => 2345)
1350
- def a.valid?(opts); false; end
1395
+ def a.validate() errors.add(:id, 'foo') end
1351
1396
  n.add_attribute(a).should == a
1352
1397
  n.remove_attribute(a).should == a
1353
1398
  end
@@ -2250,7 +2295,7 @@ describe Sequel::Model, "many_to_many" do
2250
2295
  @c2.many_to_many :attributes, :class => @c1
2251
2296
  n = @c1.new
2252
2297
  a = @c2.load(:id=>123)
2253
- def n.valid?(opts); false; end
2298
+ def n.validate() errors.add(:id, 'foo') end
2254
2299
  proc{a.add_attribute(n)}.should raise_error(Sequel::ValidationFailed)
2255
2300
  end
2256
2301
 
@@ -2259,7 +2304,7 @@ describe Sequel::Model, "many_to_many" do
2259
2304
  n = @c1.new
2260
2305
  n.raise_on_save_failure = false
2261
2306
  a = @c2.load(:id=>123)
2262
- def n.valid?(opts); false; end
2307
+ def n.validate() errors.add(:id, 'foo') end
2263
2308
  proc{a.add_attribute(n)}.should raise_error(Sequel::Error)
2264
2309
  end
2265
2310
 
@@ -2267,7 +2312,7 @@ describe Sequel::Model, "many_to_many" do
2267
2312
  @c2.many_to_many :attributes, :class => @c1, :validate=>false
2268
2313
  n = @c1.new
2269
2314
  a = @c2.load(:id=>123)
2270
- def n.valid?(opts); false; end
2315
+ def n.validate() errors.add(:id, 'foo') end
2271
2316
  a.add_attribute(n)
2272
2317
  n.new?.should == false
2273
2318
  end
@@ -2724,3 +2769,429 @@ describe Sequel::Model, " association reflection methods" do
2724
2769
  c.instance_methods.map{|x| x.to_s}.should include('parent')
2725
2770
  end
2726
2771
  end
2772
+
2773
+ describe "Filtering by associations" do
2774
+ before do
2775
+ @Album = Class.new(Sequel::Model(:albums))
2776
+ artist = @Artist = Class.new(Sequel::Model(:artists))
2777
+ tag = @Tag = Class.new(Sequel::Model(:tags))
2778
+ track = @Track = Class.new(Sequel::Model(:tracks))
2779
+ album_info = @AlbumInfo = Class.new(Sequel::Model(:album_infos))
2780
+ @Artist.columns :id, :id1, :id2
2781
+ @Tag.columns :id, :tid1, :tid2
2782
+ @Track.columns :id, :album_id, :album_id1, :album_id2
2783
+ @AlbumInfo.columns :id, :album_id, :album_id1, :album_id2
2784
+ @Album.class_eval do
2785
+ columns :id, :id1, :id2, :artist_id, :artist_id1, :artist_id2
2786
+ many_to_one :artist, :class=>artist
2787
+ one_to_many :tracks, :class=>track, :key=>:album_id
2788
+ one_to_one :album_info, :class=>album_info, :key=>:album_id
2789
+ many_to_many :tags, :class=>tag, :left_key=>:album_id, :join_table=>:albums_tags
2790
+
2791
+ many_to_one :cartist, :class=>artist, :key=>[:artist_id1, :artist_id2], :primary_key=>[:id1, :id2]
2792
+ one_to_many :ctracks, :class=>track, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2793
+ one_to_one :calbum_info, :class=>album_info, :key=>[:album_id1, :album_id2], :primary_key=>[:id1, :id2]
2794
+ many_to_many :ctags, :class=>tag, :left_key=>[:album_id1, :album_id2], :left_primary_key=>[:id1, :id2], :right_key=>[:tag_id1, :tag_id2], :right_primary_key=>[:tid1, :tid2], :join_table=>:albums_tags
2795
+ end
2796
+ end
2797
+
2798
+ it "should be able to filter on many_to_one associations" do
2799
+ @Album.filter(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (artist_id = 3)'
2800
+ end
2801
+
2802
+ it "should be able to filter on one_to_many associations" do
2803
+ @Album.filter(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (id = 3)'
2804
+ end
2805
+
2806
+ it "should be able to filter on one_to_one associations" do
2807
+ @Album.filter(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE (id = 3)'
2808
+ end
2809
+
2810
+ it "should be able to filter on many_to_many associations" do
2811
+ @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))))'
2812
+ end
2813
+
2814
+ it "should be able to filter on many_to_one associations with composite keys" do
2815
+ @Album.filter(:cartist=>@Artist.load(:id1=>3, :id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1 = 3) AND (artist_id2 = 4))'
2816
+ end
2817
+
2818
+ it "should be able to filter on one_to_many associations with composite keys" do
2819
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 = 3) AND (id2 = 4))'
2820
+ end
2821
+
2822
+ it "should be able to filter on one_to_one associations with composite keys" do
2823
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3, :album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE ((id1 = 3) AND (id2 = 4))'
2824
+ end
2825
+
2826
+ it "should be able to filter on many_to_many associations with composite keys" do
2827
+ @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))))'
2828
+ end
2829
+
2830
+ it "should work inside a complex filter" do
2831
+ artist = @Artist.load(:id=>3)
2832
+ @Album.filter{foo & {:artist=>artist}}.sql.should == 'SELECT * FROM albums WHERE (foo AND (artist_id = 3))'
2833
+ track = @Track.load(:album_id=>4)
2834
+ @Album.filter{foo & [[:artist, artist], [:tracks, track]]}.sql.should == 'SELECT * FROM albums WHERE (foo AND (artist_id = 3) AND (id = 4))'
2835
+ end
2836
+
2837
+ it "should raise for an invalid association name" do
2838
+ proc{@Album.filter(:foo=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2839
+ end
2840
+
2841
+ it "should raise for an invalid association type" do
2842
+ @Album.many_to_many :iatags, :clone=>:tags
2843
+ @Album.association_reflection(:iatags)[:type] = :foo
2844
+ proc{@Album.filter(:iatags=>@Tag.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2845
+ end
2846
+
2847
+ it "should raise for an invalid associated object class " do
2848
+ proc{@Album.filter(:tags=>@Artist.load(:id=>3)).sql}.should raise_error(Sequel::Error)
2849
+ end
2850
+
2851
+ it "should raise for an invalid associated object class when multiple objects are used" do
2852
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2853
+ end
2854
+
2855
+ it "should correctly handle case when a multiple value association is used" do
2856
+ proc{@Album.filter(:tags=>[@Tag.load(:id=>3), @Artist.load(:id=>3)]).sql}.should raise_error(Sequel::Error)
2857
+ end
2858
+
2859
+ it "should not affect non-association IN/NOT IN filtering with an empty array" do
2860
+ @Album.filter(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (tag_id != tag_id)'
2861
+ @Album.exclude(:tag_id=>[]).sql.should == 'SELECT * FROM albums WHERE (1 = 1)'
2862
+ end
2863
+
2864
+ it "should work correctly in subclasses" do
2865
+ c = Class.new(@Album)
2866
+ c.many_to_one :sartist, :class=>@Artist
2867
+ c.filter(:sartist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE (sartist_id = 3)'
2868
+ end
2869
+
2870
+ it "should be able to exclude on many_to_one associations" do
2871
+ @Album.exclude(:artist=>@Artist.load(:id=>3)).sql.should == 'SELECT * FROM albums WHERE ((artist_id != 3) OR (artist_id IS NULL))'
2872
+ end
2873
+
2874
+ it "should be able to exclude on one_to_many associations" do
2875
+ @Album.exclude(:tracks=>@Track.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
2876
+ end
2877
+
2878
+ it "should be able to exclude on one_to_one associations" do
2879
+ @Album.exclude(:album_info=>@AlbumInfo.load(:album_id=>3)).sql.should == 'SELECT * FROM albums WHERE ((id != 3) OR (id IS NULL))'
2880
+ end
2881
+
2882
+ it "should be able to exclude on many_to_many associations" do
2883
+ @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))'
2884
+ end
2885
+
2886
+ it "should be able to exclude on many_to_one associations with composite keys" do
2887
+ @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))'
2888
+ end
2889
+
2890
+ it "should be able to exclude on one_to_many associations with composite keys" do
2891
+ @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))'
2892
+ end
2893
+
2894
+ it "should be able to exclude on one_to_one associations with composite keys" do
2895
+ @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))'
2896
+ end
2897
+
2898
+ it "should be able to exclude on many_to_many associations with composite keys" do
2899
+ @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))'
2900
+ end
2901
+
2902
+ it "should be able to filter on multiple many_to_one associations" do
2903
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.load(:id=>4)]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3, 4))'
2904
+ end
2905
+
2906
+ it "should be able to filter on multiple one_to_many associations" do
2907
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
2908
+ end
2909
+
2910
+ it "should be able to filter on multiple one_to_one associations" do
2911
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.load(:album_id=>4)]).sql.should == 'SELECT * FROM albums WHERE (id IN (3, 4))'
2912
+ end
2913
+
2914
+ it "should be able to filter on multiple many_to_many associations" do
2915
+ @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))))'
2916
+ end
2917
+
2918
+ it "should be able to filter on multiple many_to_one associations with composite keys" do
2919
+ @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)))'
2920
+ end
2921
+
2922
+ it "should be able to filter on multiple one_to_many associations with composite keys" do
2923
+ @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)))'
2924
+ end
2925
+
2926
+ it "should be able to filter on multiple one_to_one associations with composite keys" do
2927
+ @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)))'
2928
+ end
2929
+
2930
+ it "should be able to filter on multiple many_to_many associations with composite keys" do
2931
+ @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))))'
2932
+ end
2933
+
2934
+ it "should be able to exclude on multiple many_to_one associations" do
2935
+ @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))'
2936
+ end
2937
+
2938
+ it "should be able to exclude on multiple one_to_many associations" do
2939
+ @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))'
2940
+ end
2941
+
2942
+ it "should be able to exclude on multiple one_to_one associations" do
2943
+ @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))'
2944
+ end
2945
+
2946
+ it "should be able to exclude on multiple many_to_many associations" do
2947
+ @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))'
2948
+ end
2949
+
2950
+ it "should be able to exclude on multiple many_to_one associations with composite keys" do
2951
+ @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))'
2952
+ end
2953
+
2954
+ it "should be able to exclude on multiple one_to_many associations with composite keys" do
2955
+ @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))'
2956
+ end
2957
+
2958
+ it "should be able to exclude on multiple one_to_one associations with composite keys" do
2959
+ @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))'
2960
+ end
2961
+
2962
+ it "should be able to exclude on multiple many_to_many associations with composite keys" do
2963
+ @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))'
2964
+ end
2965
+
2966
+ it "should be able to handle NULL values when filtering many_to_one associations" do
2967
+ @Album.filter(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2968
+ end
2969
+
2970
+ it "should be able to handle NULL values when filtering one_to_many associations" do
2971
+ @Album.filter(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2972
+ end
2973
+
2974
+ it "should be able to handle NULL values when filtering one_to_one associations" do
2975
+ @Album.filter(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2976
+ end
2977
+
2978
+ it "should be able to handle NULL values when filtering many_to_many associations" do
2979
+ @Album.filter(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2980
+ end
2981
+
2982
+ it "should be able to handle filteringing with NULL values for many_to_one associations with composite keys" do
2983
+ @Album.filter(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2984
+ @Album.filter(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2985
+ @Album.filter(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2986
+ end
2987
+
2988
+ it "should be able to filtering with NULL values for one_to_many associations with composite keys" do
2989
+ @Album.filter(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2990
+ @Album.filter(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2991
+ @Album.filter(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2992
+ end
2993
+
2994
+ it "should be able to filtering with NULL values for one_to_one associations with composite keys" do
2995
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2996
+ @Album.filter(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
2997
+ @Album.filter(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
2998
+ end
2999
+
3000
+ it "should be able to filtering with NULL values for many_to_many associations with composite keys" do
3001
+ @Album.filter(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'f\''
3002
+ @Album.filter(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'f\''
3003
+ @Album.filter(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'f\''
3004
+ end
3005
+
3006
+ it "should be able to handle NULL values when excluding many_to_one associations" do
3007
+ @Album.exclude(:artist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3008
+ end
3009
+
3010
+ it "should be able to handle NULL values when excluding one_to_many associations" do
3011
+ @Album.exclude(:tracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3012
+ end
3013
+
3014
+ it "should be able to handle NULL values when excluding one_to_one associations" do
3015
+ @Album.exclude(:album_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3016
+ end
3017
+
3018
+ it "should be able to handle NULL values when excluding many_to_many associations" do
3019
+ @Album.exclude(:tags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3020
+ end
3021
+
3022
+ it "should be able to handle excluding with NULL values for many_to_one associations with composite keys" do
3023
+ @Album.exclude(:cartist=>@Artist.load(:id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3024
+ @Album.exclude(:cartist=>@Artist.load(:id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3025
+ @Album.exclude(:cartist=>@Artist.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3026
+ end
3027
+
3028
+ it "should be able to excluding with NULL values for one_to_many associations with composite keys" do
3029
+ @Album.exclude(:ctracks=>@Track.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3030
+ @Album.exclude(:ctracks=>@Track.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3031
+ @Album.exclude(:ctracks=>@Track.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3032
+ end
3033
+
3034
+ it "should be able to excluding with NULL values for one_to_one associations with composite keys" do
3035
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3036
+ @Album.exclude(:calbum_info=>@AlbumInfo.load(:album_id1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3037
+ @Album.exclude(:calbum_info=>@AlbumInfo.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3038
+ end
3039
+
3040
+ it "should be able to excluding with NULL values for many_to_many associations with composite keys" do
3041
+ @Album.exclude(:ctags=>@Tag.load(:tid1=>3)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3042
+ @Album.exclude(:ctags=>@Tag.load(:tid2=>4)).sql.should == 'SELECT * FROM albums WHERE \'t\''
3043
+ @Album.exclude(:ctags=>@Tag.new).sql.should == 'SELECT * FROM albums WHERE \'t\''
3044
+ end
3045
+
3046
+ it "should be able to handle NULL values when filtering multiple many_to_one associations" do
3047
+ @Album.filter(:artist=>[@Artist.load(:id=>3), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (3))'
3048
+ @Album.filter(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
3049
+ end
3050
+
3051
+ it "should be able to handle NULL values when filtering multiple one_to_many associations" do
3052
+ @Album.filter(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
3053
+ @Album.filter(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
3054
+ end
3055
+
3056
+ it "should be able to handle NULL values when filtering multiple one_to_one associations" do
3057
+ @Album.filter(:album_info=>[@AlbumInfo.load(:album_id=>3), @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE (id IN (3))'
3058
+ @Album.filter(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
3059
+ end
3060
+
3061
+ it "should be able to handle NULL values when filtering multiple many_to_many associations" do
3062
+ @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))))'
3063
+ @Album.filter(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'f\''
3064
+ end
3065
+
3066
+ it "should be able to handle NULL values when filtering multiple many_to_one associations with composite keys" do
3067
+ @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)))'
3068
+ @Album.filter(:cartist=>[@Artist.load(:id1=>3, :id2=>4), @Artist.new]).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN ((3, 4)))'
3069
+ end
3070
+
3071
+ it "should be able handle NULL values when filtering multiple one_to_many associations with composite keys" do
3072
+ @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)))'
3073
+ @Album.filter(:ctracks=>[@Track.load(:album_id1=>3, :album_id2=>4), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN ((3, 4)))'
3074
+ end
3075
+
3076
+ it "should be able to handle NULL values when filtering multiple one_to_one associations with composite keys" do
3077
+ @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)))'
3078
+ @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)))'
3079
+ end
3080
+
3081
+ it "should be able to handle NULL values when filtering multiple many_to_many associations with composite keys" do
3082
+ @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))))'
3083
+ @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))))'
3084
+ end
3085
+
3086
+ it "should be able to handle NULL values when excluding multiple many_to_one associations" do
3087
+ @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))'
3088
+ @Album.exclude(:artist=>[@Artist.new, @Artist.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
3089
+ end
3090
+
3091
+ it "should be able to handle NULL values when excluding multiple one_to_many associations" do
3092
+ @Album.exclude(:tracks=>[@Track.load(:album_id=>3), @Track.new]).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (3)) OR (id IS NULL))'
3093
+ @Album.exclude(:tracks=>[@Track.new, @Track.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
3094
+ end
3095
+
3096
+ it "should be able to handle NULL values when excluding multiple one_to_one associations" do
3097
+ @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))'
3098
+ @Album.exclude(:album_info=>[@AlbumInfo.new, @AlbumInfo.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
3099
+ end
3100
+
3101
+ it "should be able to handle NULL values when excluding multiple many_to_many associations" do
3102
+ @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))'
3103
+ @Album.exclude(:tags=>[@Tag.new, @Tag.new]).sql.should == 'SELECT * FROM albums WHERE \'t\''
3104
+ end
3105
+
3106
+ it "should be able to handle NULL values when excluding multiple many_to_one associations with composite keys" do
3107
+ @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))'
3108
+ @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))'
3109
+ end
3110
+
3111
+ it "should be able handle NULL values when excluding multiple one_to_many associations with composite keys" do
3112
+ @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))'
3113
+ @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))'
3114
+ end
3115
+
3116
+ it "should be able to handle NULL values when excluding multiple one_to_one associations with composite keys" do
3117
+ @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))'
3118
+ @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))'
3119
+ end
3120
+
3121
+ it "should be able to handle NULL values when excluding multiple many_to_many associations with composite keys" do
3122
+ @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))'
3123
+ @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))'
3124
+ end
3125
+
3126
+ it "should be able to filter on many_to_one association datasets" do
3127
+ @Album.filter(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (artist_id IN (SELECT id FROM artists WHERE ((x = 1) AND (id IS NOT NULL))))'
3128
+ end
3129
+
3130
+ it "should be able to filter on one_to_many association datasets" do
3131
+ @Album.filter(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM tracks WHERE ((x = 1) AND (album_id IS NOT NULL))))'
3132
+ end
3133
+
3134
+ it "should be able to filter on one_to_one association datasets" do
3135
+ @Album.filter(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM album_infos WHERE ((x = 1) AND (album_id IS NOT NULL))))'
3136
+ end
3137
+
3138
+ it "should be able to filter on many_to_many association datasets" do
3139
+ @Album.filter(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (id IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (album_id IS NOT NULL))))'
3140
+ end
3141
+
3142
+ it "should be able to filter on many_to_one association datasets with composite keys" do
3143
+ @Album.filter(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((artist_id1, artist_id2) IN (SELECT id1, id2 FROM artists WHERE ((x = 1) AND (id1 IS NOT NULL) AND (id2 IS NOT NULL))))'
3144
+ end
3145
+
3146
+ it "should be able to filter on one_to_many association datasets with composite keys" do
3147
+ @Album.filter(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM tracks WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
3148
+ end
3149
+
3150
+ it "should be able to filter on one_to_one association datasets with composite keys" do
3151
+ @Album.filter(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM album_infos WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
3152
+ end
3153
+
3154
+ it "should be able to filter on many_to_many association datasets with composite keys" do
3155
+ @Album.filter(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id1, id2) IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN (SELECT tid1, tid2 FROM tags WHERE ((x = 1) AND (tid1 IS NOT NULL) AND (tid2 IS NOT NULL)))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL))))'
3156
+ end
3157
+
3158
+ it "should be able to exclude on many_to_one association datasets" do
3159
+ @Album.exclude(:artist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((artist_id NOT IN (SELECT id FROM artists WHERE ((x = 1) AND (id IS NOT NULL)))) OR (artist_id IS NULL))'
3160
+ end
3161
+
3162
+ it "should be able to exclude on one_to_many association datasets" do
3163
+ @Album.exclude(:tracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM tracks WHERE ((x = 1) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
3164
+ end
3165
+
3166
+ it "should be able to exclude on one_to_one association datasets" do
3167
+ @Album.exclude(:album_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM album_infos WHERE ((x = 1) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
3168
+ end
3169
+
3170
+ it "should be able to exclude on many_to_many association datasets" do
3171
+ @Album.exclude(:tags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE ((id NOT IN (SELECT album_id FROM albums_tags WHERE ((tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (album_id IS NOT NULL)))) OR (id IS NULL))'
3172
+ end
3173
+
3174
+ it "should be able to exclude on many_to_one association datasets with composite keys" do
3175
+ @Album.exclude(:cartist=>@Artist.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((artist_id1, artist_id2) NOT IN (SELECT id1, id2 FROM artists WHERE ((x = 1) AND (id1 IS NOT NULL) AND (id2 IS NOT NULL)))) OR (artist_id1 IS NULL) OR (artist_id2 IS NULL))'
3176
+ end
3177
+
3178
+ it "should be able to exclude on one_to_many association datasets with composite keys" do
3179
+ @Album.exclude(:ctracks=>@Track.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM tracks WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
3180
+ end
3181
+
3182
+ it "should be able to exclude on one_to_one association datasets with composite keys" do
3183
+ @Album.exclude(:calbum_info=>@AlbumInfo.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM album_infos WHERE ((x = 1) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
3184
+ end
3185
+
3186
+ it "should be able to exclude on many_to_many association datasets with composite keys" do
3187
+ @Album.exclude(:ctags=>@Tag.filter(:x=>1)).sql.should == 'SELECT * FROM albums WHERE (((id1, id2) NOT IN (SELECT album_id1, album_id2 FROM albums_tags WHERE (((tag_id1, tag_id2) IN (SELECT tid1, tid2 FROM tags WHERE ((x = 1) AND (tid1 IS NOT NULL) AND (tid2 IS NOT NULL)))) AND (album_id1 IS NOT NULL) AND (album_id2 IS NOT NULL)))) OR (id1 IS NULL) OR (id2 IS NULL))'
3188
+ end
3189
+
3190
+ it "should do a regular IN query if the dataset for a different model is used" do
3191
+ @Album.filter(:artist=>@Album.select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
3192
+ end
3193
+
3194
+ it "should do a regular IN query if a non-model dataset is used" do
3195
+ @Album.filter(:artist=>@Album.db.from(:albums).select(:x)).sql.should == 'SELECT * FROM albums WHERE (artist IN (SELECT x FROM albums))'
3196
+ end
3197
+ end
@@ -131,6 +131,50 @@ describe Sequel::Model, ".def_dataset_method" do
131
131
  end
132
132
  end
133
133
 
134
+ describe Sequel::Model, ".dataset_module" do
135
+ before do
136
+ @c = Class.new(Sequel::Model(:items))
137
+ end
138
+
139
+ it "should extend the dataset with the module if the model has a dataset" do
140
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
141
+ @c.dataset.return_3.should == 3
142
+ end
143
+
144
+ it "should add methods defined in the module to the class" do
145
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
146
+ @c.return_3.should == 3
147
+ end
148
+
149
+ it "should cache calls and readd methods if set_dataset is used" do
150
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
151
+ @c.set_dataset :items
152
+ @c.return_3.should == 3
153
+ @c.dataset.return_3.should == 3
154
+ end
155
+
156
+ it "should readd methods to subclasses, if set_dataset is used in a subclass" do
157
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
158
+ c = Class.new(@c)
159
+ c.set_dataset :items
160
+ c.return_3.should == 3
161
+ c.dataset.return_3.should == 3
162
+ end
163
+
164
+ it "should only have a single dataset_module per class" do
165
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
166
+ @c.instance_eval{dataset_module{def return_3() 3 + (begin; super; rescue NoMethodError; 1; end) end}}
167
+ @c.return_3.should == 4
168
+ end
169
+
170
+ it "should not have subclasses share the dataset_module" do
171
+ @c.instance_eval{dataset_module{def return_3() 3 end}}
172
+ c = Class.new(@c)
173
+ c.instance_eval{dataset_module{def return_3() 3 + (begin; super; rescue NoMethodError; 1; end) end}}
174
+ c.return_3.should == 6
175
+ end
176
+ end
177
+
134
178
  describe "A model class with implicit table name" do
135
179
  before do
136
180
  class Donkey < Sequel::Model
@@ -258,7 +302,7 @@ describe Sequel::Model, ".(allowed|restricted)_columns " do
258
302
  before do
259
303
  @c = Class.new(Sequel::Model(:blahblah)) do
260
304
  columns :x, :y, :z
261
- def _refresh(ds)
305
+ def _save_refresh
262
306
  self
263
307
  end
264
308
  end
@@ -485,3 +529,49 @@ describe Sequel::Model, ".[] optimization" do
485
529
  @c[1]
486
530
  end
487
531
  end
532
+
533
+ describe "Model datasets #with_pk" do
534
+ before do
535
+ @c = Class.new(Sequel::Model(:a))
536
+ @ds = @c.dataset
537
+ def @ds.fetch_rows(sql)
538
+ db << sql
539
+ yield(:id=>1)
540
+ end
541
+ @sqls = MODEL_DB.sqls
542
+ @sqls.clear
543
+ end
544
+
545
+ it "should return the first record where the primary key matches" do
546
+ @ds.with_pk(1).should == @c.load(:id=>1)
547
+ @sqls.should == ["SELECT * FROM a WHERE (a.id = 1) LIMIT 1"]
548
+ end
549
+
550
+ it "should handle existing filters" do
551
+ @ds.filter(:a=>2).with_pk(1)
552
+ @sqls.should == ["SELECT * FROM a WHERE ((a = 2) AND (a.id = 1)) LIMIT 1"]
553
+ end
554
+
555
+ it "should work with string values" do
556
+ @ds.with_pk("foo")
557
+ @sqls.should == ["SELECT * FROM a WHERE (a.id = 'foo') LIMIT 1"]
558
+ end
559
+
560
+ it "should handle an array for composite primary keys" do
561
+ @c.set_primary_key :id1, :id2
562
+ @ds.with_pk([1, 2])
563
+ ["SELECT * FROM a WHERE ((a.id1 = 1) AND (a.id2 = 2)) LIMIT 1",
564
+ "SELECT * FROM a WHERE ((a.id2 = 2) AND (a.id1 = 1)) LIMIT 1"].should include(@sqls.first)
565
+ @sqls.length.should == 1
566
+ end
567
+
568
+ it "should have #[] consider an integer as a primary key lookup" do
569
+ @ds[1].should == @c.load(:id=>1)
570
+ @sqls.should == ["SELECT * FROM a WHERE (a.id = 1) LIMIT 1"]
571
+ end
572
+
573
+ it "should not have #[] consider a string as a primary key lookup" do
574
+ @ds['foo'].should == @c.load(:id=>1)
575
+ @sqls.should == ["SELECT * FROM a WHERE (foo) LIMIT 1"]
576
+ end
577
+ end