sequel 3.23.0 → 3.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG +64 -0
  2. data/doc/association_basics.rdoc +43 -5
  3. data/doc/model_hooks.rdoc +64 -27
  4. data/doc/prepared_statements.rdoc +8 -4
  5. data/doc/reflection.rdoc +8 -2
  6. data/doc/release_notes/3.23.0.txt +1 -1
  7. data/doc/release_notes/3.24.0.txt +420 -0
  8. data/lib/sequel/adapters/db2.rb +8 -1
  9. data/lib/sequel/adapters/firebird.rb +25 -9
  10. data/lib/sequel/adapters/informix.rb +4 -19
  11. data/lib/sequel/adapters/jdbc.rb +34 -17
  12. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  13. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  14. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  15. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  16. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  18. data/lib/sequel/adapters/postgres.rb +30 -1
  19. data/lib/sequel/adapters/shared/access.rb +10 -0
  20. data/lib/sequel/adapters/shared/informix.rb +45 -0
  21. data/lib/sequel/adapters/shared/mssql.rb +82 -8
  22. data/lib/sequel/adapters/shared/mysql.rb +25 -7
  23. data/lib/sequel/adapters/shared/postgres.rb +39 -6
  24. data/lib/sequel/adapters/shared/sqlite.rb +57 -5
  25. data/lib/sequel/adapters/sqlite.rb +8 -3
  26. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  27. data/lib/sequel/ast_transformer.rb +190 -0
  28. data/lib/sequel/core.rb +1 -1
  29. data/lib/sequel/database/misc.rb +6 -0
  30. data/lib/sequel/database/query.rb +33 -3
  31. data/lib/sequel/database/schema_methods.rb +6 -2
  32. data/lib/sequel/dataset/features.rb +6 -0
  33. data/lib/sequel/dataset/prepared_statements.rb +17 -2
  34. data/lib/sequel/dataset/query.rb +17 -0
  35. data/lib/sequel/dataset/sql.rb +2 -53
  36. data/lib/sequel/exceptions.rb +4 -0
  37. data/lib/sequel/extensions/to_dot.rb +95 -83
  38. data/lib/sequel/model.rb +5 -0
  39. data/lib/sequel/model/associations.rb +80 -14
  40. data/lib/sequel/model/base.rb +182 -55
  41. data/lib/sequel/model/exceptions.rb +3 -1
  42. data/lib/sequel/plugins/association_pks.rb +6 -4
  43. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  44. data/lib/sequel/plugins/many_through_many.rb +8 -3
  45. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  46. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  47. data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
  48. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  49. data/lib/sequel/sql.rb +8 -0
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +43 -18
  52. data/spec/core/connection_pool_spec.rb +56 -77
  53. data/spec/core/database_spec.rb +25 -0
  54. data/spec/core/dataset_spec.rb +127 -16
  55. data/spec/core/expression_filters_spec.rb +13 -0
  56. data/spec/core/schema_spec.rb +6 -1
  57. data/spec/extensions/association_pks_spec.rb +7 -0
  58. data/spec/extensions/defaults_setter_spec.rb +64 -0
  59. data/spec/extensions/many_through_many_spec.rb +60 -4
  60. data/spec/extensions/nested_attributes_spec.rb +1 -0
  61. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  62. data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
  63. data/spec/extensions/prepared_statements_spec.rb +72 -0
  64. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  65. data/spec/extensions/to_dot_spec.rb +3 -5
  66. data/spec/integration/associations_test.rb +155 -1
  67. data/spec/integration/dataset_test.rb +8 -1
  68. data/spec/integration/plugin_test.rb +119 -0
  69. data/spec/integration/prepared_statement_test.rb +72 -1
  70. data/spec/integration/schema_test.rb +66 -8
  71. data/spec/integration/transaction_test.rb +40 -0
  72. data/spec/model/associations_spec.rb +349 -8
  73. data/spec/model/base_spec.rb +59 -0
  74. data/spec/model/hooks_spec.rb +161 -0
  75. data/spec/model/record_spec.rb +24 -0
  76. metadata +21 -4
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "prepared_statements_with_pk plugin" do
4
+ before do
5
+ @c = Class.new(Sequel::Model(:people))
6
+ @c.columns :id, :name, :i
7
+ @ds = ds = @c.dataset
8
+ def ds.fetch_rows(sql)
9
+ db << {:server=>@opts[:server] || :read_only}.merge(opts)[:server]
10
+ super{|h|}
11
+ yield(:id=>1, :name=>'foo', :i=>2)
12
+ end
13
+ @c.plugin :prepared_statements_with_pk
14
+ @p = @c.load(:id=>1, :name=>'foo', :i=>2)
15
+ @c.db.execute 'foo'
16
+ @sqls = @c.db.sqls
17
+ @sqls.clear
18
+ end
19
+
20
+ specify "should load the prepared_statements plugin" do
21
+ @c.plugins.should include(Sequel::Plugins::PreparedStatements)
22
+ end
23
+
24
+ specify "should correctly lookup by primary key from dataset" do
25
+ @c.dataset.filter(:name=>'foo')[1].should == @p
26
+ @sqls.should == [:read_only, "SELECT * FROM people WHERE ((name = 'foo') AND (id = 1)) LIMIT 1"]
27
+ end
28
+
29
+ specify "should still work correctly if there are multiple conflicting variables" do
30
+ @c.dataset.filter(:name=>'foo').or(:name=>'bar')[1].should == @p
31
+ @sqls.should == [:read_only, "SELECT * FROM people WHERE (((name = 'foo') OR (name = 'bar')) AND (id = 1)) LIMIT 1"]
32
+ end
33
+
34
+ specify "should still work correctly if the primary key is used elsewhere in the query" do
35
+ @c.dataset.filter{id > 2}[1].should == @p
36
+ @sqls.should == [:read_only, "SELECT * FROM people WHERE ((id > 2) AND (id = 1)) LIMIT 1"]
37
+ end
38
+ end
@@ -2,9 +2,7 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe Sequel::Model, "to_dot extension" do
4
4
  def dot(ds)
5
- a = []
6
- ds.send(:_to_dot, a, "", 0, ds, 0)
7
- a[2..-1]
5
+ Sequel::ToDot.new(ds).instance_variable_get(:@dot)[4...-1]
8
6
  end
9
7
 
10
8
  before do
@@ -123,7 +121,7 @@ END
123
121
  end
124
122
 
125
123
  it "should handle SQL::Subscript" do
126
- dot(@ds.select(:a.sql_subscript(1))).should == ["1 -> 2 [label=\"select\"];", "2 [label=\"Array\"];", "2 -> 3 [label=\"0\"];", "3 [label=\"Subscript: a\"];", "3 -> 4 [label=\"f\"];", "4 [label=\":a\"];", "3 -> 5 [label=\"sub\"];", "5 [label=\"Array\"];", "5 -> 6 [label=\"0\"];", "6 [label=\"1\"];"]
124
+ dot(@ds.select(:a.sql_subscript(1))).should == ["1 -> 2 [label=\"select\"];", "2 [label=\"Array\"];", "2 -> 3 [label=\"0\"];", "3 [label=\"Subscript\"];", "3 -> 4 [label=\"f\"];", "4 [label=\":a\"];", "3 -> 5 [label=\"sub\"];", "5 [label=\"Array\"];", "5 -> 6 [label=\"0\"];", "6 [label=\"1\"];"]
127
125
  end
128
126
 
129
127
  it "should handle SQL::WindowFunction" do
@@ -135,7 +133,7 @@ END
135
133
  end
136
134
 
137
135
  it "should handle JOIN ON" do
138
- dot(@ds.from(:a).join(:d, :b=>:c)).should == ["1 -> 2 [label=\"from\"];", "2 [label=\"Array\"];", "2 -> 3 [label=\"0\"];", "3 [label=\":a\"];", "1 -> 4 [label=\"join\"];", "4 [label=\"Array\"];", "4 -> 5 [label=\"0\"];", "5 [label=\"INNER JOIN ON\"];", "5 -> 6 [label=\"table\"];", "6 [label=\":d\"];", "5 -> 7 [label=\"on\"];", "7 [label=\"Array\"];", "7 -> 8 [label=\"0\"];", "8 [label=\"Array\"];", "8 -> 9 [label=\"0\"];", "9 [label=\"QualifiedIdentifier\"];", "9 -> 10 [label=\"table\"];", "10 [label=\"\\\"d\\\"\"];", "9 -> 11 [label=\"column\"];", "11 [label=\"\\\"b\\\"\"];", "8 -> 12 [label=\"1\"];", "12 [label=\"QualifiedIdentifier\"];", "12 -> 13 [label=\"table\"];", "13 [label=\"\\\"a\\\"\"];", "12 -> 14 [label=\"column\"];", "14 [label=\"\\\"c\\\"\"];"]
136
+ dot(@ds.from(:a).join(:d, :b=>:c)).should == ["1 -> 2 [label=\"from\"];", "2 [label=\"Array\"];", "2 -> 3 [label=\"0\"];", "3 [label=\":a\"];", "1 -> 4 [label=\"join\"];", "4 [label=\"Array\"];", "4 -> 5 [label=\"0\"];", "5 [label=\"INNER JOIN ON\"];", "5 -> 6 [label=\"table\"];", "6 [label=\":d\"];", "5 -> 7 [label=\"on\"];", "7 [label=\"ComplexExpression: =\"];", "7 -> 8 [label=\"0\"];", "8 [label=\"QualifiedIdentifier\"];", "8 -> 9 [label=\"table\"];", "9 [label=\"\\\"d\\\"\"];", "8 -> 10 [label=\"column\"];", "10 [label=\"\\\"b\\\"\"];", "7 -> 11 [label=\"1\"];", "11 [label=\"QualifiedIdentifier\"];", "11 -> 12 [label=\"table\"];", "12 [label=\"\\\"a\\\"\"];", "11 -> 13 [label=\"column\"];", "13 [label=\"\\\"c\\\"\"];"]
139
137
  end
140
138
 
141
139
  it "should handle JOIN USING" do
@@ -22,7 +22,7 @@ shared_examples_for "regular and composite key associations" do
22
22
  @tag.albums.should == [@album]
23
23
  end
24
24
 
25
- specify "should work correctly when filtering by associations" do
25
+ specify "should work correctly with prepared_statements_association plugin" do
26
26
  @album.update(:artist => @artist)
27
27
  @album.add_tag(@tag)
28
28
 
@@ -30,6 +30,17 @@ shared_examples_for "regular and composite key associations" do
30
30
  @artist.reload
31
31
  @tag.reload
32
32
 
33
+ [Tag, Album, Artist].each{|x| x.plugin :prepared_statements_associations}
34
+ @album.artist.should == @artist
35
+ @artist.albums.should == [@album]
36
+ @album.tags.should == [@tag]
37
+ @tag.albums.should == [@album]
38
+ end
39
+
40
+ specify "should work correctly when filtering by associations" do
41
+ @album.update(:artist => @artist)
42
+ @album.add_tag(@tag)
43
+
33
44
  Artist.filter(:albums=>@album).all.should == [@artist]
34
45
  Album.filter(:artist=>@artist).all.should == [@album]
35
46
  Album.filter(:tags=>@tag).all.should == [@album]
@@ -37,7 +48,146 @@ shared_examples_for "regular and composite key associations" do
37
48
  Album.filter(:artist=>@artist, :tags=>@tag).all.should == [@album]
38
49
  @artist.albums_dataset.filter(:tags=>@tag).all.should == [@album]
39
50
  end
51
+
52
+ specify "should work correctly when excluding by associations" do
53
+ @album.update(:artist => @artist)
54
+ @album.add_tag(@tag)
55
+ album, artist, tag = @pr.call
56
+
57
+ Artist.exclude(:albums=>@album).all.should == [artist]
58
+ Album.exclude(:artist=>@artist).all.should == [album]
59
+ Album.exclude(:tags=>@tag).all.should == [album]
60
+ Tag.exclude(:albums=>@album).all.should == [tag]
61
+ Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [album]
62
+ end
63
+
64
+ specify "should work correctly when filtering by multiple associations" do
65
+ album, artist, tag = @pr.call
66
+ @album.update(:artist => @artist)
67
+ @album.add_tag(@tag)
68
+
69
+ Artist.filter(:albums=>[@album, album]).all.should == [@artist]
70
+ Album.filter(:artist=>[@artist, artist]).all.should == [@album]
71
+ Album.filter(:tags=>[@tag, tag]).all.should == [@album]
72
+ Tag.filter(:albums=>[@album, album]).all.should == [@tag]
73
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
74
+ @artist.albums_dataset.filter(:tags=>[@tag, tag]).all.should == [@album]
75
+
76
+ album.add_tag(tag)
77
+
78
+ Artist.filter(:albums=>[@album, album]).all.should == [@artist]
79
+ Album.filter(:artist=>[@artist, artist]).all.should == [@album]
80
+ Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
81
+ Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
82
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
83
+
84
+ album.update(:artist => artist)
85
+
86
+ Artist.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
87
+ Album.filter(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
88
+ Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
89
+ Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
90
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
91
+ end
92
+
93
+ specify "should work correctly when excluding by multiple associations" do
94
+ album, artist, tag = @pr.call
95
+
96
+ Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
97
+ Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
98
+ Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
99
+ Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
100
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
101
+
102
+ @album.update(:artist => @artist)
103
+ @album.add_tag(@tag)
104
+
105
+ Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
106
+ Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [album]
107
+ Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
108
+ Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [tag]
109
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
110
+
111
+ album.add_tag(tag)
112
+
113
+ Artist.exclude(:albums=>[@album, album]).all.should == [artist]
114
+ Album.exclude(:artist=>[@artist, artist]).all.should == [album]
115
+ Album.exclude(:tags=>[@tag, tag]).all.should == []
116
+ Tag.exclude(:albums=>[@album, album]).all.should == []
117
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [album]
118
+
119
+ album.update(:artist => artist)
120
+
121
+ Artist.exclude(:albums=>[@album, album]).all.should == []
122
+ Album.exclude(:artist=>[@artist, artist]).all.should == []
123
+ Album.exclude(:tags=>[@tag, tag]).all.should == []
124
+ Tag.exclude(:albums=>[@album, album]).all.should == []
125
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == []
126
+ end
40
127
 
128
+ specify "should work correctly when excluding by associations in regards to NULL values" do
129
+ Artist.exclude(:albums=>@album).all.should == [@artist]
130
+ Album.exclude(:artist=>@artist).all.should == [@album]
131
+ Album.exclude(:tags=>@tag).all.should == [@album]
132
+ Tag.exclude(:albums=>@album).all.should == [@tag]
133
+ Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [@album]
134
+
135
+ @album.update(:artist => @artist)
136
+ @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
137
+ end
138
+
139
+ specify "should handle NULL values in join table correctly when filtering/excluding many_to_many associations" do
140
+ @ins.call
141
+ Album.exclude(:tags=>@tag).all.should == [@album]
142
+ @album.add_tag(@tag)
143
+ Album.filter(:tags=>@tag).all.should == [@album]
144
+ album, artist, tag = @pr.call
145
+ Album.exclude(:tags=>@tag).all.should == [album]
146
+ Album.exclude(:tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
147
+ end
148
+
149
+ specify "should work correctly when filtering by association datasets" do
150
+ album, artist, tag = @pr.call
151
+ @album.update(:artist => @artist)
152
+ @album.add_tag(@tag)
153
+ album.add_tag(tag)
154
+ album.update(:artist => artist)
155
+
156
+ Artist.filter(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == [@artist, artist]
157
+ Artist.filter(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
158
+ Artist.filter(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
159
+ Album.filter(:artist=>Artist.dataset).all.sort_by{|x| x.pk}.should == [@album, album]
160
+ Album.filter(:artist=>Artist.dataset.filter(Array(Artist.primary_key).zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [album]
161
+ Album.filter(:artist=>Artist.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
162
+ Album.filter(:tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == [@album, album]
163
+ Album.filter(:tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
164
+ Album.filter(:tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
165
+ Tag.filter(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == [@tag, tag]
166
+ Tag.filter(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [tag]
167
+ Tag.filter(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
168
+ end
169
+
170
+ specify "should work correctly when excluding by association datasets" do
171
+ album, artist, tag = @pr.call
172
+ @album.update(:artist => @artist)
173
+ @album.add_tag(@tag)
174
+ album.add_tag(tag)
175
+ album.update(:artist => artist)
176
+
177
+ Artist.exclude(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == []
178
+ Artist.exclude(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
179
+ Artist.exclude(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
180
+ Album.exclude(:artist=>Artist.dataset).all.sort_by{|x| x.pk}.should == []
181
+ Album.exclude(:artist=>Artist.dataset.filter(Array(Artist.primary_key).zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [@album]
182
+ Album.exclude(:artist=>Artist.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
183
+ Album.exclude(:tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == []
184
+ Album.exclude(:tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
185
+ Album.exclude(:tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
186
+ Tag.exclude(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == []
187
+ Tag.exclude(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@tag]
188
+ Tag.exclude(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@tag, tag]
189
+ end
190
+
41
191
  specify "should have remove methods work" do
42
192
  @album.update(:artist => @artist)
43
193
  @album.add_tag(@tag)
@@ -167,6 +317,8 @@ describe "Sequel::Model Simple Associations" do
167
317
  @album = Album.create(:name=>'Al')
168
318
  @artist = Artist.create(:name=>'Ar')
169
319
  @tag = Tag.create(:name=>'T')
320
+ @pr = lambda{[Album.create(:name=>'Al2'),Artist.create(:name=>'Ar2'),Tag.create(:name=>'T2')]}
321
+ @ins = lambda{@db[:albums_tags].insert(:tag_id=>@tag.id)}
170
322
  end
171
323
  after do
172
324
  @db.drop_table(:albums_tags, :tags, :albums, :artists)
@@ -333,6 +485,8 @@ describe "Sequel::Model Composite Key Associations" do
333
485
  @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
334
486
  @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
335
487
  @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
488
+ @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)]}
489
+ @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
336
490
  end
337
491
  after do
338
492
  @db.drop_table(:albums_tags, :tags, :albums, :artists)
@@ -86,6 +86,13 @@ describe "Simple Dataset operations" do
86
86
  specify "should alias columns correctly" do
87
87
  @ds.select(:id___x, :number___n).first.should == {:x=>1, :n=>10}
88
88
  end
89
+
90
+ specify "should handle true/false properly" do
91
+ @ds.filter(Sequel::TRUE).select_map(:number).should == [10]
92
+ @ds.filter(Sequel::FALSE).select_map(:number).should == []
93
+ @ds.filter(true).select_map(:number).should == [10]
94
+ @ds.filter(false).select_map(:number).should == []
95
+ end
89
96
  end
90
97
 
91
98
  describe Sequel::Dataset do
@@ -471,7 +478,7 @@ describe Sequel::SQL::Constants do
471
478
  @c2 = proc{|v| v.is_a?(Date) ? v : Date.parse(v) }
472
479
  end
473
480
  after do
474
- @db.drop_table(:constants)
481
+ @db.drop_table(:constants) if @db.table_exists?(:constants)
475
482
  end
476
483
 
477
484
  cspecify "should have working CURRENT_DATE", [:odbc, :mssql], [:jdbc, :sqlite] do
@@ -196,6 +196,12 @@ describe "Many Through Many Plugin" do
196
196
  Artist[3].albums.map{|x| x.name}.sort.should == %w'B C'
197
197
  Artist[4].albums.map{|x| x.name}.sort.should == %w'B D'
198
198
 
199
+ Artist.plugin :prepared_statements_associations
200
+ Artist[1].albums.map{|x| x.name}.sort.should == %w'A D'
201
+ Artist[2].albums.map{|x| x.name}.sort.should == %w'A C'
202
+ Artist[3].albums.map{|x| x.name}.sort.should == %w'B C'
203
+ Artist[4].albums.map{|x| x.name}.sort.should == %w'B D'
204
+
199
205
  Artist.filter(:id=>1).eager(:albums).all.map{|x| x.albums.map{|a| a.name}}.flatten.sort.should == %w'A D'
200
206
  Artist.filter(:id=>2).eager(:albums).all.map{|x| x.albums.map{|a| a.name}}.flatten.sort.should == %w'A C'
201
207
  Artist.filter(:id=>3).eager(:albums).all.map{|x| x.albums.map{|a| a.name}}.flatten.sort.should == %w'B C'
@@ -210,6 +216,20 @@ describe "Many Through Many Plugin" do
210
216
  Artist.filter(:albums=>@album2).all.map{|a| a.name}.sort.should == %w'3 4'
211
217
  Artist.filter(:albums=>@album3).all.map{|a| a.name}.sort.should == %w'2 3'
212
218
  Artist.filter(:albums=>@album4).all.map{|a| a.name}.sort.should == %w'1 4'
219
+
220
+ Artist.exclude(:albums=>@album1).all.map{|a| a.name}.sort.should == %w'3 4'
221
+ Artist.exclude(:albums=>@album2).all.map{|a| a.name}.sort.should == %w'1 2'
222
+ Artist.exclude(:albums=>@album3).all.map{|a| a.name}.sort.should == %w'1 4'
223
+ Artist.exclude(:albums=>@album4).all.map{|a| a.name}.sort.should == %w'2 3'
224
+
225
+ Artist.filter(:albums=>[@album1, @album3]).all.map{|a| a.name}.sort.should == %w'1 2 3'
226
+ Artist.filter(:albums=>[@album2, @album4]).all.map{|a| a.name}.sort.should == %w'1 3 4'
227
+
228
+ Artist.exclude(:albums=>[@album1, @album3]).all.map{|a| a.name}.sort.should == %w'4'
229
+ Artist.exclude(:albums=>[@album2, @album4]).all.map{|a| a.name}.sort.should == %w'2'
230
+
231
+ Artist.filter(:albums=>Album.filter(:id=>[@album1.id, @album3.id])).all.map{|a| a.name}.sort.should == %w'1 2 3'
232
+ Artist.exclude(:albums=>Album.filter(:id=>[@album1.id, @album3.id])).all.map{|a| a.name}.sort.should == %w'4'
213
233
  end
214
234
 
215
235
  specify "should handle typical case with 3 join tables" do
@@ -219,6 +239,12 @@ describe "Many Through Many Plugin" do
219
239
  Artist[3].related_artists.map{|x| x.name}.sort.should == %w'2 3 4'
220
240
  Artist[4].related_artists.map{|x| x.name}.sort.should == %w'1 3 4'
221
241
 
242
+ Artist.plugin :prepared_statements_associations
243
+ Artist[1].related_artists.map{|x| x.name}.sort.should == %w'1 2 4'
244
+ Artist[2].related_artists.map{|x| x.name}.sort.should == %w'1 2 3'
245
+ Artist[3].related_artists.map{|x| x.name}.sort.should == %w'2 3 4'
246
+ Artist[4].related_artists.map{|x| x.name}.sort.should == %w'1 3 4'
247
+
222
248
  Artist.filter(:id=>1).eager(:related_artists).all.map{|x| x.related_artists.map{|a| a.name}}.flatten.sort.should == %w'1 2 4'
223
249
  Artist.filter(:id=>2).eager(:related_artists).all.map{|x| x.related_artists.map{|a| a.name}}.flatten.sort.should == %w'1 2 3'
224
250
  Artist.filter(:id=>3).eager(:related_artists).all.map{|x| x.related_artists.map{|a| a.name}}.flatten.sort.should == %w'2 3 4'
@@ -233,6 +259,17 @@ describe "Many Through Many Plugin" do
233
259
  Artist.filter(:related_artists=>@artist2).all.map{|a| a.name}.sort.should == %w'1 2 3'
234
260
  Artist.filter(:related_artists=>@artist3).all.map{|a| a.name}.sort.should == %w'2 3 4'
235
261
  Artist.filter(:related_artists=>@artist4).all.map{|a| a.name}.sort.should == %w'1 3 4'
262
+
263
+ Artist.exclude(:related_artists=>@artist1).all.map{|a| a.name}.sort.should == %w'3'
264
+ Artist.exclude(:related_artists=>@artist2).all.map{|a| a.name}.sort.should == %w'4'
265
+ Artist.exclude(:related_artists=>@artist3).all.map{|a| a.name}.sort.should == %w'1'
266
+ Artist.exclude(:related_artists=>@artist4).all.map{|a| a.name}.sort.should == %w'2'
267
+
268
+ Artist.filter(:related_artists=>[@artist1, @artist4]).all.map{|a| a.name}.sort.should == %w'1 2 3 4'
269
+ Artist.exclude(:related_artists=>[@artist1, @artist4]).all.map{|a| a.name}.sort.should == %w''
270
+
271
+ Artist.filter(:related_artists=>Artist.filter(:id=>@artist1.id)).all.map{|a| a.name}.sort.should == %w'1 2 4'
272
+ Artist.exclude(:related_artists=>Artist.filter(:id=>@artist1.id)).all.map{|a| a.name}.sort.should == %w'3'
236
273
  end
237
274
 
238
275
  specify "should handle extreme case with 5 join tables" do
@@ -251,6 +288,12 @@ describe "Many Through Many Plugin" do
251
288
  Artist[3].related_albums.map{|x| x.name}.sort.should == %w'A B D'
252
289
  Artist[4].related_albums.map{|x| x.name}.sort.should == %w'B D'
253
290
 
291
+ Artist.plugin :prepared_statements_associations
292
+ Artist[1].related_albums.map{|x| x.name}.sort.should == %w'A B C'
293
+ Artist[2].related_albums.map{|x| x.name}.sort.should == %w'A B C D'
294
+ Artist[3].related_albums.map{|x| x.name}.sort.should == %w'A B D'
295
+ Artist[4].related_albums.map{|x| x.name}.sort.should == %w'B D'
296
+
254
297
  Artist.filter(:id=>1).eager(:related_albums).all.map{|x| x.related_albums.map{|a| a.name}}.flatten.sort.should == %w'A B C'
255
298
  Artist.filter(:id=>2).eager(:related_albums).all.map{|x| x.related_albums.map{|a| a.name}}.flatten.sort.should == %w'A B C D'
256
299
  Artist.filter(:id=>3).eager(:related_albums).all.map{|x| x.related_albums.map{|a| a.name}}.flatten.sort.should == %w'A B D'
@@ -265,6 +308,20 @@ describe "Many Through Many Plugin" do
265
308
  Artist.filter(:related_albums=>@album2).all.map{|a| a.name}.sort.should == %w'1 2 3 4'
266
309
  Artist.filter(:related_albums=>@album3).all.map{|a| a.name}.sort.should == %w'1 2'
267
310
  Artist.filter(:related_albums=>@album4).all.map{|a| a.name}.sort.should == %w'2 3 4'
311
+
312
+ Artist.exclude(:related_albums=>@album1).all.map{|a| a.name}.sort.should == %w'4'
313
+ Artist.exclude(:related_albums=>@album2).all.map{|a| a.name}.sort.should == %w''
314
+ Artist.exclude(:related_albums=>@album3).all.map{|a| a.name}.sort.should == %w'3 4'
315
+ Artist.exclude(:related_albums=>@album4).all.map{|a| a.name}.sort.should == %w'1'
316
+
317
+ Artist.filter(:related_albums=>[@album1, @album3]).all.map{|a| a.name}.sort.should == %w'1 2 3'
318
+ Artist.filter(:related_albums=>[@album3, @album4]).all.map{|a| a.name}.sort.should == %w'1 2 3 4'
319
+
320
+ Artist.exclude(:related_albums=>[@album1, @album3]).all.map{|a| a.name}.sort.should == %w'4'
321
+ Artist.exclude(:related_albums=>[@album2, @album4]).all.map{|a| a.name}.sort.should == %w''
322
+
323
+ Artist.filter(:related_albums=>Album.filter(:id=>[@album1.id, @album3.id])).all.map{|a| a.name}.sort.should == %w'1 2 3'
324
+ Artist.exclude(:related_albums=>Album.filter(:id=>[@album1.id, @album3.id])).all.map{|a| a.name}.sort.should == %w'4'
268
325
  end
269
326
  end
270
327
 
@@ -1321,3 +1378,65 @@ describe "Sequel::Plugins::Tree" do
1321
1378
  end
1322
1379
  end
1323
1380
  end
1381
+
1382
+ describe "Sequel::Plugins::PreparedStatements" do
1383
+ before do
1384
+ @db = INTEGRATION_DB
1385
+ @db.create_table!(:ps_test) do
1386
+ primary_key :id
1387
+ String :name
1388
+ Integer :i
1389
+ end
1390
+ @c = Class.new(Sequel::Model(@db[:ps_test]))
1391
+ @foo = @c.create(:name=>'foo', :i=>10)
1392
+ @bar = @c.create(:name=>'bar', :i=>20)
1393
+ @c.plugin :prepared_statements_with_pk
1394
+ end
1395
+ after do
1396
+ @db.drop_table(:ps_test)
1397
+ end
1398
+
1399
+ it "should work with looking up using Model.[]" do
1400
+ @c[@foo.id].should == @foo
1401
+ @c[@bar.id].should == @bar
1402
+ @c[0].should == nil
1403
+ end
1404
+
1405
+ it "should work with looking up using Dataset#with_pk" do
1406
+ @c.dataset.with_pk(@foo.id).should == @foo
1407
+ @c.dataset.with_pk(@bar.id).should == @bar
1408
+ @c.dataset.with_pk(0).should == nil
1409
+
1410
+ @c.dataset.filter(:i=>0).with_pk(@foo.id).should == nil
1411
+ @c.dataset.filter(:i=>10).with_pk(@foo.id).should == @foo
1412
+ @c.dataset.filter(:i=>20).with_pk(@bar.id).should == @bar
1413
+ @c.dataset.filter(:name=>'foo').with_pk(@foo.id).should == @foo
1414
+ @c.dataset.filter(:name=>'bar').with_pk(@bar.id).should == @bar
1415
+ @c.dataset.filter(:name=>'baz').with_pk(@bar.id).should == nil
1416
+ end
1417
+
1418
+ it "should work with Model#destroy" do
1419
+ @foo.destroy
1420
+ @bar.destroy
1421
+ @c[@foo.id].should == nil
1422
+ @c[@bar.id].should == nil
1423
+ end
1424
+
1425
+ it "should work with Model#update" do
1426
+ @foo.update(:name=>'foo2', :i=>30)
1427
+ @c[@foo.id].should == @c.load(:id=>@foo.id, :name=>'foo2', :i=>30)
1428
+ @foo.update(:name=>'foo3')
1429
+ @c[@foo.id].should == @c.load(:id=>@foo.id, :name=>'foo3', :i=>30)
1430
+ @foo.update(:i=>40)
1431
+ @c[@foo.id].should == @c.load(:id=>@foo.id, :name=>'foo3', :i=>40)
1432
+ end
1433
+
1434
+ it "should work with Model#create" do
1435
+ o = @c.create(:name=>'foo2', :i=>30)
1436
+ @c[o.id].should == @c.load(:id=>o.id, :name=>'foo2', :i=>30)
1437
+ o = @c.create(:name=>'foo2')
1438
+ @c[o.id].should == @c.load(:id=>o.id, :name=>'foo2', :i=>nil)
1439
+ o = @c.create(:i=>30)
1440
+ @c[o.id].should == @c.load(:id=>o.id, :name=>nil, :i=>30)
1441
+ end
1442
+ end
@@ -251,7 +251,7 @@ describe "Bound Argument Types" do
251
251
  @ds.filter(:t=>@ds.ba(:$x, :timestamp)).prepare(:first, :ps_time).call(:x=>@vs[:t])[:t].should == @vs[:t]
252
252
  end
253
253
 
254
- cspecify "should handle blob type", [:swift] do
254
+ cspecify "should handle blob type", [:swift], [:odbc] do
255
255
  @ds.filter(:file=>@ds.ba(:$x, :bytea)).prepare(:first, :ps_blob).call(:x=>@vs[:file])[:file].should == @vs[:file]
256
256
  end
257
257
 
@@ -268,3 +268,74 @@ describe "Bound Argument Types" do
268
268
  end
269
269
  end unless INTEGRATION_DB.adapter_scheme == :swift && INTEGRATION_DB.database_type == :postgres
270
270
 
271
+ describe "Dataset#unbind" do
272
+ before do
273
+ @ds = ds = INTEGRATION_DB[:items]
274
+ @ct = proc do |t, v|
275
+ INTEGRATION_DB.create_table!(:items) do
276
+ column :c, t
277
+ end
278
+ ds.insert(:c=>v)
279
+ end
280
+ @u = proc{|ds| ds, bv = ds.unbind; ds.call(:first, bv)}
281
+ end
282
+ after do
283
+ INTEGRATION_DB.drop_table(:items) rescue nil
284
+ end
285
+
286
+ specify "should unbind values assigned to equality and inequality statements" do
287
+ @ct[Integer, 10]
288
+ @u[@ds.filter(:c=>10)].should == {:c=>10}
289
+ @u[@ds.exclude(:c=>10)].should == nil
290
+ @u[@ds.filter{c < 10}].should == nil
291
+ @u[@ds.filter{c <= 10}].should == {:c=>10}
292
+ @u[@ds.filter{c > 10}].should == nil
293
+ @u[@ds.filter{c >= 10}].should == {:c=>10}
294
+ end
295
+
296
+ cspecify "should handle numerics and strings", [:odbc], [:swift, :sqlite] do
297
+ @ct[Integer, 10]
298
+ @u[@ds.filter(:c=>10)].should == {:c=>10}
299
+ @ct[Float, 0.0]
300
+ @u[@ds.filter{c < 1}].should == {:c=>0.0}
301
+ @ct[String, 'foo']
302
+ @u[@ds.filter(:c=>'foo')].should == {:c=>'foo'}
303
+
304
+ INTEGRATION_DB.create_table!(:items) do
305
+ BigDecimal :c, :size=>[15,2]
306
+ end
307
+ @ds.insert(:c=>BigDecimal.new('1.1'))
308
+ @u[@ds.filter{c > 0}].should == {:c=>BigDecimal.new('1.1')}
309
+ end
310
+
311
+ cspecify "should handle dates and times", [:sqlite], [:do], [:jdbc, :mssql], [:tinytds] do
312
+ @ct[Date, Date.today]
313
+ @u[@ds.filter(:c=>Date.today)].should == {:c=>Date.today}
314
+ t = Time.now
315
+ @ct[Time, t]
316
+ @u[@ds.filter{c < t + 1}][:c].to_i.should == t.to_i
317
+ end
318
+
319
+ specify "should handle QualifiedIdentifiers" do
320
+ @ct[Integer, 10]
321
+ @u[@ds.filter{items__c > 1}].should == {:c=>10}
322
+ end
323
+
324
+ specify "should handle deep nesting" do
325
+ INTEGRATION_DB.create_table!(:items) do
326
+ Integer :a
327
+ Integer :b
328
+ Integer :c
329
+ Integer :d
330
+ end
331
+ @ds.insert(:a=>2, :b=>0, :c=>3, :d=>5)
332
+ @u[@ds.filter{a > 1}.and{b < 2}.or(:c=>3).and({~{:d=>4}=>1}.case(0) => 1)].should == {:a=>2, :b=>0, :c=>3, :d=>5}
333
+ @u[@ds.filter{a > 1}.and{b < 2}.or(:c=>3).and({~{:d=>5}=>1}.case(0) => 1)].should == nil
334
+ end
335
+
336
+ specify "should handle case where the same variable has the same value in multiple places " do
337
+ @ct[Integer, 1]
338
+ @u[@ds.filter{c > 1}.or{c < 1}.invert].should == {:c=>1}
339
+ @u[@ds.filter{c > 1}.or{c < 1}].should == nil
340
+ end
341
+ end