sequel 3.23.0 → 3.24.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 (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