sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -60,12 +60,6 @@ describe "Dataset" do
60
60
  db = Sequel::Database.new(:identifier_output_method=>:downcase)
61
61
  db[:a].identifier_output_method.should == :downcase
62
62
  end
63
- end
64
-
65
- describe "Dataset" do
66
- before do
67
- @dataset = Sequel::Dataset.new("db")
68
- end
69
63
 
70
64
  specify "should have quote_identifiers= method which changes literalization of identifiers" do
71
65
  @dataset.quote_identifiers = true
@@ -111,27 +105,25 @@ describe "Dataset#clone" do
111
105
 
112
106
  specify "should create an exact copy of the dataset" do
113
107
  @dataset.row_proc = Proc.new{|r| r}
114
- @clone = @dataset.clone
108
+ clone = @dataset.clone
115
109
 
116
- @clone.object_id.should_not === @dataset.object_id
117
- @clone.class.should == @dataset.class
118
- @clone.opts.should == @dataset.opts
119
- @clone.row_proc.should == @dataset.row_proc
110
+ clone.object_id.should_not === @dataset.object_id
111
+ clone.class.should == @dataset.class
112
+ clone.db.should == @dataset.db
113
+ clone.opts.should == @dataset.opts
114
+ clone.row_proc.should == @dataset.row_proc
120
115
  end
121
116
 
122
117
  specify "should deep-copy the dataset opts" do
123
- @clone = @dataset.clone
118
+ clone = @dataset.clone
124
119
 
125
- @clone.opts.should_not equal(@dataset.opts)
120
+ clone.opts.should_not equal(@dataset.opts)
126
121
  @dataset.filter!(:a => 'b')
127
- @clone.opts[:filter].should be_nil
128
- end
129
-
130
- specify "should return a clone self" do
131
- clone = @dataset.clone({})
132
- clone.class.should == @dataset.class
133
- clone.db.should == @dataset.db
134
- clone.opts.should == @dataset.opts
122
+ clone.opts[:filter].should be_nil
123
+
124
+ clone = @dataset.clone(:from => [:other])
125
+ @dataset.opts[:from].should == [:items]
126
+ clone.opts[:from].should == [:other]
135
127
  end
136
128
 
137
129
  specify "should merge the specified options" do
@@ -144,12 +136,6 @@ describe "Dataset#clone" do
144
136
  clone.opts.should == {:from => [:other]}
145
137
  end
146
138
 
147
- specify "should create a clone with a deep copy of options" do
148
- clone = @dataset.clone(:from => [:other])
149
- @dataset.opts[:from].should == [:items]
150
- clone.opts[:from].should == [:other]
151
- end
152
-
153
139
  specify "should return an object with the same modules included" do
154
140
  m = Module.new do
155
141
  def __xyz__; "xyz"; end
@@ -161,7 +147,7 @@ end
161
147
 
162
148
  describe "Dataset#==" do
163
149
  before do
164
- @db = MockDatabase.new
150
+ @db = Sequel.mock
165
151
  @h = {}
166
152
  end
167
153
 
@@ -170,7 +156,7 @@ describe "Dataset#==" do
170
156
  end
171
157
 
172
158
  specify "should be different for datasets with different dbs" do
173
- @db[:t].should_not == MockDatabase.new[:t]
159
+ @db[:t].should_not == Sequel.mock[:t]
174
160
  end
175
161
 
176
162
  specify "should be different for datasets with different opts" do
@@ -186,7 +172,7 @@ end
186
172
 
187
173
  describe "Dataset#hash" do
188
174
  before do
189
- @db = MockDatabase.new
175
+ @db = Sequel.mock
190
176
  @h = {}
191
177
  end
192
178
 
@@ -197,7 +183,7 @@ describe "Dataset#hash" do
197
183
  end
198
184
 
199
185
  specify "should be different for datasets with different dbs" do
200
- @db[:t].hash.should_not == MockDatabase.new[:t].hash
186
+ @db[:t].hash.should_not == Sequel.mock[:t].hash
201
187
  end
202
188
 
203
189
  specify "should be different for datasets with different opts" do
@@ -232,6 +218,12 @@ describe "A simple dataset" do
232
218
  @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
233
219
  end
234
220
 
221
+ specify "should use a single column with a default value when the dataset doesn't support using insert statement with default values" do
222
+ @dataset.meta_def(:insert_supports_empty_values?){false}
223
+ @dataset.meta_def(:columns){[:a, :b]}
224
+ @dataset.insert_sql.should == 'INSERT INTO test (b) VALUES (DEFAULT)'
225
+ end
226
+
235
227
  specify "should format an insert statement with hash" do
236
228
  @dataset.insert_sql(:name => 'wxyz', :price => 342).
237
229
  should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
@@ -245,8 +237,6 @@ describe "A simple dataset" do
245
237
  end
246
238
 
247
239
  specify "should format an insert statement with an object that respond_to? :values" do
248
- dbb = Sequel::Database.new
249
-
250
240
  v = Object.new
251
241
  def v.values; {:a => 1}; end
252
242
 
@@ -261,29 +251,24 @@ describe "A simple dataset" do
261
251
  end
262
252
 
263
253
  specify "should format an insert statement with sub-query" do
264
- @sub = Sequel::Dataset.new(nil).from(:something).filter(:x => 2)
265
- @dataset.insert_sql(@sub).should == \
266
- "INSERT INTO test SELECT * FROM something WHERE (x = 2)"
254
+ @dataset.insert_sql(@dataset.from(:something).filter(:x => 2)).should == "INSERT INTO test SELECT * FROM something WHERE (x = 2)"
267
255
  end
268
256
 
269
257
  specify "should format an insert statement with array" do
270
- @dataset.insert_sql('a', 2, 6.5).should ==
271
- "INSERT INTO test VALUES ('a', 2, 6.5)"
258
+ @dataset.insert_sql('a', 2, 6.5).should == "INSERT INTO test VALUES ('a', 2, 6.5)"
272
259
  end
273
260
 
274
261
  specify "should format an update statement" do
275
- @dataset.update_sql(:name => 'abc').should ==
276
- "UPDATE test SET name = 'abc'"
262
+ @dataset.update_sql(:name => 'abc').should == "UPDATE test SET name = 'abc'"
277
263
  end
278
264
 
279
265
  specify "should be able to return rows for arbitrary SQL" do
280
- @dataset.clone(:sql => 'xxx yyy zzz').select_sql.should ==
281
- "xxx yyy zzz"
266
+ @dataset.clone(:sql => 'xxx yyy zzz').select_sql.should == "xxx yyy zzz"
282
267
  end
283
268
 
284
269
  specify "should use the :sql option for all sql methods" do
285
270
  sql = "X"
286
- ds = Sequel::Dataset.new(nil, :sql=>sql)
271
+ ds = @dataset.clone(:sql=>sql)
287
272
  ds.sql.should == sql
288
273
  ds.select_sql.should == sql
289
274
  ds.insert_sql.should == sql
@@ -418,7 +403,7 @@ describe "Dataset#where" do
418
403
  b.should == a
419
404
  end
420
405
 
421
- specify "should not replace named placeholders that don't existin in the hash" do
406
+ specify "should not replace named placeholders that don't exist in the hash" do
422
407
  @dataset.where('price < :price AND id in :ids', :price=>100).select_sql.should ==
423
408
  "SELECT * FROM test WHERE (price < 100 AND id in :ids)"
424
409
  end
@@ -538,34 +523,33 @@ describe "Dataset#where" do
538
523
 
539
524
  specify "should handle IN/NOT IN queries with multiple columns and a dataset where the database doesn't support it" do
540
525
  @dataset.meta_def(:supports_multiple_column_in?){false}
541
- d1 = @d1.select(:id1, :id2)
542
- def d1.fetch_rows(sql)
543
- @sql_used = sql
544
- @columns = [:id1, :id2]
545
- yield(:id1=>1, :id2=>2)
546
- yield(:id1=>3, :id2=>4)
547
- end
548
- d1.instance_variable_get(:@sql_used).should == nil
526
+ db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
527
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
549
528
  @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
550
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
551
- d1.instance_variable_set(:@sql_used, nil)
529
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
552
530
  @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
553
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
531
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
554
532
  end
555
533
 
556
534
  specify "should handle IN/NOT IN queries with multiple columns and an empty dataset where the database doesn't support it" do
557
535
  @dataset.meta_def(:supports_multiple_column_in?){false}
558
- d1 = @d1.select(:id1, :id2)
559
- def d1.fetch_rows(sql)
560
- @sql_used = sql
561
- @columns = [:id1, :id2]
562
- end
563
- d1.instance_variable_get(:@sql_used).should == nil
536
+ db = Sequel.mock
537
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
564
538
  @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE ((id1 != id1) AND (id2 != id2))"
565
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
566
- d1.instance_variable_set(:@sql_used, nil)
539
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
567
540
  @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (1 = 1)"
568
- d1.instance_variable_get(:@sql_used).should == "SELECT id1, id2 FROM test WHERE (region = 'Asia')"
541
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
542
+ end
543
+
544
+ specify "should handle IN/NOT IN queries for datasets with row_procs" do
545
+ @dataset.meta_def(:supports_multiple_column_in?){false}
546
+ db = Sequel.mock(:fetch=>[{:id1=>1, :id2=>2}, {:id1=>3, :id2=>4}])
547
+ d1 = db[:test].select(:id1, :id2).filter(:region=>'Asia').columns(:id1, :id2)
548
+ d1.row_proc = proc{|h| Object.new}
549
+ @dataset.filter([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 = 1) AND (id2 = 2)) OR ((id1 = 3) AND (id2 = 4)))"
550
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
551
+ @dataset.exclude([:id1, :id2] => d1).sql.should == "SELECT * FROM test WHERE (((id1 != 1) OR (id2 != 2)) AND ((id1 != 3) OR (id2 != 4)))"
552
+ db.sqls.should == ["SELECT id1, id2 FROM test WHERE (region = 'Asia')"]
569
553
  end
570
554
 
571
555
  specify "should accept a subquery for an EXISTS clause" do
@@ -601,10 +585,18 @@ describe "Dataset#where" do
601
585
  end
602
586
 
603
587
  specify "should accept true and false as arguments" do
604
- @dataset.filter(true).sql.should ==
605
- "SELECT * FROM test WHERE 't'"
606
- @dataset.filter(false).sql.should ==
607
- "SELECT * FROM test WHERE 'f'"
588
+ @dataset.filter(true).sql.should == "SELECT * FROM test WHERE 't'"
589
+ @dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE 't'"
590
+ @dataset.filter(false).sql.should == "SELECT * FROM test WHERE 'f'"
591
+ @dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE 'f'"
592
+ end
593
+
594
+ specify "should use boolean expression if dataset does not support where true/false" do
595
+ def @dataset.supports_where_true?() false end
596
+ @dataset.filter(true).sql.should == "SELECT * FROM test WHERE (1 = 1)"
597
+ @dataset.filter(Sequel::SQLTRUE).sql.should == "SELECT * FROM test WHERE (1 = 1)"
598
+ @dataset.filter(false).sql.should == "SELECT * FROM test WHERE (1 = 0)"
599
+ @dataset.filter(Sequel::SQLFALSE).sql.should == "SELECT * FROM test WHERE (1 = 0)"
608
600
  end
609
601
 
610
602
  specify "should allow the use of multiple arguments" do
@@ -658,33 +650,25 @@ describe "Dataset#or" do
658
650
  end
659
651
 
660
652
  specify "should add an alternative expression to the where clause" do
661
- @d1.or(:y => 2).sql.should ==
662
- 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
653
+ @d1.or(:y => 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
663
654
  end
664
655
 
665
656
  specify "should accept all forms of filters" do
666
- @d1.or('y > ?', 2).sql.should ==
667
- 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
668
- @d1.or(:yy.sql_number > 3).sql.should ==
669
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
657
+ @d1.or('y > ?', 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
658
+ @d1.or(:yy.sql_number > 3).sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
670
659
  end
671
660
 
672
661
  specify "should accept blocks passed to filter" do
673
- @d1.or{:yy.sql_number > 3}.sql.should ==
674
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
662
+ @d1.or{:yy.sql_number > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
675
663
  end
676
664
 
677
665
  specify "should correctly add parens to give predictable results" do
678
- @d1.filter(:y => 2).or(:z => 3).sql.should ==
679
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
680
-
681
- @d1.or(:y => 2).filter(:z => 3).sql.should ==
682
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
666
+ @d1.filter(:y => 2).or(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
667
+ @d1.or(:y => 2).filter(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
683
668
  end
684
669
 
685
670
  specify "should allow the use of blocks and arguments simultaneously" do
686
- @d1.or(:zz.sql_number < 3){:yy.sql_number > 3}.sql.should ==
687
- 'SELECT * FROM test WHERE ((x = 1) OR ((zz < 3) AND (yy > 3)))'
671
+ @d1.or(:zz.sql_number < 3){:yy.sql_number > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) OR ((zz < 3) AND (yy > 3)))'
688
672
  end
689
673
  end
690
674
 
@@ -697,34 +681,25 @@ describe "Dataset#and" do
697
681
  specify "should raise if no filter exists" do
698
682
  proc {@dataset.and(:a => 1)}.should raise_error(Sequel::Error)
699
683
  proc {@dataset.where(:a => 1).group(:t).and(:b => 2)}.should_not raise_error(Sequel::Error)
700
- @dataset.where(:a => 1).group(:t).and(:b => 2).sql ==
701
- "SELECT * FROM test WHERE (a = 1) AND (b = 2) GROUP BY t"
684
+ @dataset.where(:a => 1).group(:t).and(:b => 2).sql == "SELECT * FROM test WHERE (a = 1) AND (b = 2) GROUP BY t"
702
685
  end
703
686
 
704
687
  specify "should add an alternative expression to the where clause" do
705
- @d1.and(:y => 2).sql.should ==
706
- 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
688
+ @d1.and(:y => 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
707
689
  end
708
690
 
709
- specify "should accept all forms of filters" do
710
- # probably not exhaustive, but good enough
711
- @d1.and('y > ?', 2).sql.should ==
712
- 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
713
- @d1.and(:yy.sql_number > 3).sql.should ==
714
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
691
+ specify "should accept different types of filters" do
692
+ @d1.and('y > ?', 2).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
693
+ @d1.and(:yy.sql_number > 3).sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
715
694
  end
716
695
 
717
696
  specify "should accept blocks passed to filter" do
718
- @d1.and {:yy.sql_number > 3}.sql.should ==
719
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
697
+ @d1.and {:yy.sql_number > 3}.sql.should == 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
720
698
  end
721
699
 
722
700
  specify "should correctly add parens to give predictable results" do
723
- @d1.or(:y => 2).and(:z => 3).sql.should ==
724
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
725
-
726
- @d1.and(:y => 2).or(:z => 3).sql.should ==
727
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
701
+ @d1.or(:y => 2).and(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
702
+ @d1.and(:y => 2).or(:z => 3).sql.should == 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
728
703
  end
729
704
  end
730
705
 
@@ -734,8 +709,11 @@ describe "Dataset#exclude" do
734
709
  end
735
710
 
736
711
  specify "should correctly negate the expression when one condition is given" do
737
- @dataset.exclude(:region=>'Asia').select_sql.should ==
738
- "SELECT * FROM test WHERE (region != 'Asia')"
712
+ @dataset.exclude(:region=>'Asia').select_sql.should == "SELECT * FROM test WHERE (region != 'Asia')"
713
+ end
714
+
715
+ specify "should affect the having clause if having clause is already used" do
716
+ @dataset.group_and_count(:name).having{count > 2}.exclude{count > 5}.sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name HAVING ((count > 2) AND (count <= 5))"
739
717
  end
740
718
 
741
719
  specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
@@ -745,30 +723,46 @@ describe "Dataset#exclude" do
745
723
  end
746
724
 
747
725
  specify "should parenthesize a single string condition correctly" do
748
- @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should ==
749
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
726
+ @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should == "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
750
727
  end
751
728
 
752
729
  specify "should parenthesize an array condition correctly" do
753
- @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should ==
754
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
730
+ @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should == "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
755
731
  end
756
732
 
757
733
  specify "should correctly parenthesize when it is used twice" do
758
- @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should ==
759
- "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
734
+ @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should == "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
760
735
  end
761
736
 
762
737
  specify "should support proc expressions" do
763
- @dataset.exclude{:id.sql_number < 6}.sql.should ==
764
- 'SELECT * FROM test WHERE (id >= 6)'
738
+ @dataset.exclude{:id.sql_number < 6}.sql.should == 'SELECT * FROM test WHERE (id >= 6)'
765
739
  end
766
740
 
767
741
  specify "should allow the use of blocks and arguments simultaneously" do
768
- @dataset.exclude(:id => (7..11)){:id.sql_number < 6}.sql.should ==
769
- 'SELECT * FROM test WHERE ((id < 7) OR (id > 11) OR (id >= 6))'
770
- @dataset.exclude([:id, 1], [:x, 3]){:id.sql_number < 6}.sql.should ==
771
- 'SELECT * FROM test WHERE ((id != 1) OR (x != 3) OR (id >= 6))'
742
+ @dataset.exclude(:id => (7..11)){:id.sql_number < 6}.sql.should == 'SELECT * FROM test WHERE ((id < 7) OR (id > 11) OR (id >= 6))'
743
+ @dataset.exclude([:id, 1], [:x, 3]){:id.sql_number < 6}.sql.should == 'SELECT * FROM test WHERE ((id != 1) OR (x != 3) OR (id >= 6))'
744
+ end
745
+ end
746
+
747
+ describe "Dataset#exclude_where" do
748
+ before do
749
+ @dataset = Sequel::Dataset.new(nil).from(:test)
750
+ end
751
+
752
+ specify "should correctly negate the expression and add it to the where clause" do
753
+ @dataset.exclude_where(:region=>'Asia').sql.should == "SELECT * FROM test WHERE (region != 'Asia')"
754
+ @dataset.exclude_where(:region=>'Asia').exclude_where(:region=>'NA').sql.should == "SELECT * FROM test WHERE ((region != 'Asia') AND (region != 'NA'))"
755
+ end
756
+
757
+ specify "should affect the where clause even if having clause is already used" do
758
+ @dataset.group_and_count(:name).having{count > 2}.exclude_where(:region=>'Asia').sql.should ==
759
+ "SELECT name, count(*) AS count FROM test WHERE (region != 'Asia') GROUP BY name HAVING (count > 2)"
760
+ end
761
+ end
762
+
763
+ describe "Dataset#exclude_having" do
764
+ specify "should correctly negate the expression and add it to the having clause" do
765
+ Sequel::Dataset.new(nil).from(:test).exclude_having{count > 2}.exclude_having{count < 0}.sql.should == "SELECT * FROM test HAVING ((count <= 2) AND (count >= 0))"
772
766
  end
773
767
  end
774
768
 
@@ -794,9 +788,6 @@ describe "Dataset#having" do
794
788
  before do
795
789
  @dataset = Sequel::Dataset.new(nil).from(:test)
796
790
  @grouped = @dataset.group(:region).select(:region, :sum.sql_function(:population), :avg.sql_function(:gdp))
797
- @d1 = @grouped.having('sum(population) > 10')
798
- @d2 = @grouped.having(:region => 'Asia')
799
- @columns = "region, sum(population), avg(gdp)"
800
791
  end
801
792
 
802
793
  specify "should just clone if given an empty argument" do
@@ -806,18 +797,15 @@ describe "Dataset#having" do
806
797
  end
807
798
 
808
799
  specify "should affect select statements" do
809
- @d1.select_sql.should ==
810
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
800
+ @grouped.having('sum(population) > 10').select_sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
811
801
  end
812
802
 
813
803
  specify "should support proc expressions" do
814
- @grouped.having {:sum.sql_function(:population) > 10}.sql.should ==
815
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
804
+ @grouped.having {:sum.sql_function(:population) > 10}.sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING (sum(population) > 10)"
816
805
  end
817
806
 
818
807
  specify "should work with and on the having clause" do
819
- @grouped.having( :a.sql_number > 1 ).and( :b.sql_number < 2 ).sql.should ==
820
- "SELECT #{@columns} FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
808
+ @grouped.having( :a.sql_number > 1 ).and( :b.sql_number < 2 ).sql.should == "SELECT region, sum(population), avg(gdp) FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
821
809
  end
822
810
  end
823
811
 
@@ -848,7 +836,7 @@ describe "a grouped dataset" do
848
836
  end
849
837
 
850
838
  specify "should format the right statement for counting (as a subquery)" do
851
- db = MockDatabase.new
839
+ db = Sequel.mock
852
840
  db[:test].select(:name).group(:name).count
853
841
  db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT name FROM test GROUP BY name) AS t1 LIMIT 1"]
854
842
  end
@@ -868,45 +856,43 @@ describe "Dataset#group_by" do
868
856
  end
869
857
 
870
858
  specify "should specify the grouping in generated select statement" do
871
- @dataset.select_sql.should ==
872
- "SELECT * FROM test GROUP BY type_id"
873
- @dataset.group_by(:a, :b).select_sql.should ==
874
- "SELECT * FROM test GROUP BY a, b"
875
- @dataset.group_by(:type_id=>nil).select_sql.should ==
876
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
859
+ @dataset.select_sql.should == "SELECT * FROM test GROUP BY type_id"
860
+ @dataset.group_by(:a, :b).select_sql.should == "SELECT * FROM test GROUP BY a, b"
861
+ @dataset.group_by(:type_id=>nil).select_sql.should == "SELECT * FROM test GROUP BY (type_id IS NULL)"
877
862
  end
878
863
 
879
- specify "should ungroup when passed nil, empty, or no arguments" do
880
- @dataset.group_by.select_sql.should ==
881
- "SELECT * FROM test"
882
- @dataset.group_by(nil).select_sql.should ==
883
- "SELECT * FROM test"
864
+ specify "should ungroup when passed nil or no arguments" do
865
+ @dataset.group_by.select_sql.should == "SELECT * FROM test"
866
+ @dataset.group_by(nil).select_sql.should == "SELECT * FROM test"
884
867
  end
885
868
 
886
869
  specify "should undo previous grouping" do
887
- @dataset.group_by(:a).group_by(:b).select_sql.should ==
888
- "SELECT * FROM test GROUP BY b"
889
- @dataset.group_by(:a, :b).group_by.select_sql.should ==
890
- "SELECT * FROM test"
870
+ @dataset.group_by(:a).group_by(:b).select_sql.should == "SELECT * FROM test GROUP BY b"
871
+ @dataset.group_by(:a, :b).group_by.select_sql.should == "SELECT * FROM test"
891
872
  end
892
873
 
893
874
  specify "should be aliased as #group" do
894
- @dataset.group(:type_id=>nil).select_sql.should ==
895
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
875
+ @dataset.group(:type_id=>nil).select_sql.should == "SELECT * FROM test GROUP BY (type_id IS NULL)"
876
+ end
877
+
878
+ specify "should take a virtual row block" do
879
+ @dataset.group{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
880
+ @dataset.group_by{type_id > 1}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1)"
881
+ @dataset.group{[type_id > 1, type_id < 2]}.sql.should == "SELECT * FROM test GROUP BY (type_id > 1), (type_id < 2)"
882
+ @dataset.group(:foo){type_id > 1}.sql.should == "SELECT * FROM test GROUP BY foo, (type_id > 1)"
896
883
  end
897
884
  end
898
885
 
899
886
  describe "Dataset#as" do
900
887
  specify "should set up an alias" do
901
888
  dataset = Sequel::Dataset.new(nil).from(:test)
902
- dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == \
903
- 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
889
+ dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
904
890
  end
905
891
  end
906
892
 
907
893
  describe "Dataset#literal" do
908
894
  before do
909
- @dataset = Sequel::Dataset.new(nil).from(:test)
895
+ @dataset = Sequel::Database.new.from(:test)
910
896
  end
911
897
 
912
898
  specify "should escape strings properly" do
@@ -965,6 +951,12 @@ describe "Dataset#literal" do
965
951
  d.literal(d).should == "(#{d.sql})"
966
952
  end
967
953
 
954
+ specify "should literalize Sequel::SQLTime properly" do
955
+ t = Sequel::SQLTime.now
956
+ s = t.strftime("'%H:%M:%S")
957
+ @dataset.literal(t).should == "#{s}.#{sprintf('%06i', t.usec)}'"
958
+ end
959
+
968
960
  specify "should literalize Time properly" do
969
961
  t = Time.now
970
962
  s = t.strftime("'%Y-%m-%d %H:%M:%S")
@@ -1069,35 +1061,25 @@ describe "Dataset#from" do
1069
1061
  end
1070
1062
 
1071
1063
  specify "should format a Dataset as a subquery if it has had options set" do
1072
- @dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should ==
1073
- "SELECT * FROM (SELECT * FROM a WHERE (a = 1)) AS t1"
1064
+ @dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should == "SELECT * FROM (SELECT * FROM a WHERE (a = 1)) AS t1"
1074
1065
  end
1075
1066
 
1076
1067
  specify "should automatically alias sub-queries" do
1077
- @dataset.from(@dataset.from(:a).group(:b)).select_sql.should ==
1078
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1"
1068
+ @dataset.from(@dataset.from(:a).group(:b)).select_sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1"
1079
1069
 
1080
1070
  d1 = @dataset.from(:a).group(:b)
1081
1071
  d2 = @dataset.from(:c).group(:d)
1082
-
1083
- @dataset.from(d1, d2).sql.should ==
1084
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1, (SELECT * FROM c GROUP BY d) AS t2"
1072
+ @dataset.from(d1, d2).sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS t1, (SELECT * FROM c GROUP BY d) AS t2"
1085
1073
  end
1086
1074
 
1087
1075
  specify "should accept a hash for aliasing" do
1088
- @dataset.from(:a => :b).sql.should ==
1089
- "SELECT * FROM a AS b"
1090
-
1091
- @dataset.from(:a => 'b').sql.should ==
1092
- "SELECT * FROM a AS b"
1093
-
1094
- @dataset.from(@dataset.from(:a).group(:b) => :c).sql.should ==
1095
- "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
1076
+ @dataset.from(:a => :b).sql.should == "SELECT * FROM a AS b"
1077
+ @dataset.from(:a => 'b').sql.should == "SELECT * FROM a AS b"
1078
+ @dataset.from(@dataset.from(:a).group(:b) => :c).sql.should == "SELECT * FROM (SELECT * FROM a GROUP BY b) AS c"
1096
1079
  end
1097
1080
 
1098
1081
  specify "should always use a subquery if given a dataset" do
1099
- @dataset.from(@dataset.from(:a)).select_sql.should ==
1100
- "SELECT * FROM (SELECT * FROM a) AS t1"
1082
+ @dataset.from(@dataset.from(:a)).select_sql.should == "SELECT * FROM (SELECT * FROM a) AS t1"
1101
1083
  end
1102
1084
 
1103
1085
  specify "should remove all FROM tables if called with no arguments" do
@@ -1105,32 +1087,27 @@ describe "Dataset#from" do
1105
1087
  end
1106
1088
 
1107
1089
  specify "should accept sql functions" do
1108
- @dataset.from(:abc.sql_function(:def)).select_sql.should ==
1109
- "SELECT * FROM abc(def)"
1110
-
1111
- @dataset.from(:a.sql_function(:i)).select_sql.should ==
1112
- "SELECT * FROM a(i)"
1090
+ @dataset.from(:abc.sql_function(:def)).select_sql.should == "SELECT * FROM abc(def)"
1091
+ @dataset.from(:a.sql_function(:i)).select_sql.should == "SELECT * FROM a(i)"
1113
1092
  end
1114
1093
 
1115
1094
  specify "should accept :schema__table___alias symbol format" do
1116
- @dataset.from(:abc__def).select_sql.should ==
1117
- "SELECT * FROM abc.def"
1118
- @dataset.from(:a_b__c).select_sql.should ==
1119
- "SELECT * FROM a_b.c"
1120
- @dataset.from(:'#__#').select_sql.should ==
1121
- 'SELECT * FROM #.#'
1122
- @dataset.from(:abc__def___d).select_sql.should ==
1123
- "SELECT * FROM abc.def AS d"
1124
- @dataset.from(:a_b__d_e___f_g).select_sql.should ==
1125
- "SELECT * FROM a_b.d_e AS f_g"
1126
- @dataset.from(:'#__#___#').select_sql.should ==
1127
- 'SELECT * FROM #.# AS #'
1128
- @dataset.from(:abc___def).select_sql.should ==
1129
- "SELECT * FROM abc AS def"
1130
- @dataset.from(:a_b___c_d).select_sql.should ==
1131
- "SELECT * FROM a_b AS c_d"
1132
- @dataset.from(:'#___#').select_sql.should ==
1133
- 'SELECT * FROM # AS #'
1095
+ @dataset.from(:abc__def).select_sql.should == "SELECT * FROM abc.def"
1096
+ @dataset.from(:a_b__c).select_sql.should == "SELECT * FROM a_b.c"
1097
+ @dataset.from(:'#__#').select_sql.should == 'SELECT * FROM #.#'
1098
+ @dataset.from(:abc__def___d).select_sql.should == "SELECT * FROM abc.def AS d"
1099
+ @dataset.from(:a_b__d_e___f_g).select_sql.should == "SELECT * FROM a_b.d_e AS f_g"
1100
+ @dataset.from(:'#__#___#').select_sql.should == 'SELECT * FROM #.# AS #'
1101
+ @dataset.from(:abc___def).select_sql.should == "SELECT * FROM abc AS def"
1102
+ @dataset.from(:a_b___c_d).select_sql.should == "SELECT * FROM a_b AS c_d"
1103
+ @dataset.from(:'#___#').select_sql.should == 'SELECT * FROM # AS #'
1104
+ end
1105
+
1106
+ specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
1107
+ @dataset.meta_def(:supports_cte?){true}
1108
+ @dataset.meta_def(:supports_cte_in_subselect?){false}
1109
+ @dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b))).sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
1110
+ @dataset.from(@dataset.from(:a).with(:a, @dataset.from(:b)), @dataset.from(:c).with(:c, @dataset.from(:d))).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a) AS t1, (SELECT * FROM c) AS t2'
1134
1111
  end
1135
1112
  end
1136
1113
 
@@ -1147,18 +1124,17 @@ describe "Dataset#select" do
1147
1124
  specify "should accept symbols and literal strings" do
1148
1125
  @d.select('aaa'.lit).sql.should == 'SELECT aaa FROM test'
1149
1126
  @d.select(:a, 'b'.lit).sql.should == 'SELECT a, b FROM test'
1150
- @d.select(:test__cc, 'test.d AS e'.lit).sql.should ==
1151
- 'SELECT test.cc, test.d AS e FROM test'
1152
- @d.select('test.d AS e'.lit, :test__cc).sql.should ==
1153
- 'SELECT test.d AS e, test.cc FROM test'
1154
-
1155
- # symbol helpers
1156
- @d.select(:test.*).sql.should ==
1157
- 'SELECT test.* FROM test'
1158
- @d.select(:test__name.as(:n)).sql.should ==
1159
- 'SELECT test.name AS n FROM test'
1160
- @d.select(:test__name___n).sql.should ==
1161
- 'SELECT test.name AS n FROM test'
1127
+ @d.select(:test__cc, 'test.d AS e'.lit).sql.should == 'SELECT test.cc, test.d AS e FROM test'
1128
+ @d.select('test.d AS e'.lit, :test__cc).sql.should == 'SELECT test.d AS e, test.cc FROM test'
1129
+ @d.select(:test__name___n).sql.should == 'SELECT test.name AS n FROM test'
1130
+ end
1131
+
1132
+ specify "should accept ColumnAlls" do
1133
+ @d.select(:test.*).sql.should == 'SELECT test.* FROM test'
1134
+ end
1135
+
1136
+ specify "should accept QualifiedIdentifiers" do
1137
+ @d.select(:test__name.as(:n)).sql.should == 'SELECT test.name AS n FROM test'
1162
1138
  end
1163
1139
 
1164
1140
  specify "should use the wildcard if no arguments are given" do
@@ -1166,20 +1142,17 @@ describe "Dataset#select" do
1166
1142
  end
1167
1143
 
1168
1144
  specify "should accept a hash for AS values" do
1169
- @d.select(:name => 'n', :__ggh => 'age').sql.should =~
1170
- /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
1145
+ @d.select(:name => 'n', :__ggh => 'age').sql.should =~ /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
1171
1146
  end
1172
1147
 
1173
- specify "should overrun the previous select option" do
1148
+ specify "should override the previous select option" do
1174
1149
  @d.select!(:a, :b, :c).select.sql.should == 'SELECT * FROM test'
1175
1150
  @d.select!(:price).select(:name).sql.should == 'SELECT name FROM test'
1176
1151
  end
1177
1152
 
1178
1153
  specify "should accept arbitrary objects and literalize them correctly" do
1179
1154
  @d.select(1, :a, 't').sql.should == "SELECT 1, a, 't' FROM test"
1180
-
1181
1155
  @d.select(nil, :sum.sql_function(:t), :x___y).sql.should == "SELECT NULL, sum(t), x AS y FROM test"
1182
-
1183
1156
  @d.select(nil, 1, :x => :y).sql.should == "SELECT NULL, 1, x AS y FROM test"
1184
1157
  end
1185
1158
 
@@ -1198,6 +1171,27 @@ describe "Dataset#select" do
1198
1171
  end
1199
1172
  end
1200
1173
 
1174
+ describe "Dataset#select_group" do
1175
+ before do
1176
+ @d = Sequel::Dataset.new(nil).from(:test)
1177
+ end
1178
+
1179
+ specify "should set both SELECT and GROUP" do
1180
+ @d.select_group(:name).sql.should == 'SELECT name FROM test GROUP BY name'
1181
+ @d.select_group(:a, :b__c, :d___e).sql.should == 'SELECT a, b.c, d AS e FROM test GROUP BY a, b.c, d'
1182
+ end
1183
+
1184
+ specify "should remove from both SELECT and GROUP if no arguments" do
1185
+ @d.select_group(:name).select_group.sql.should == 'SELECT * FROM test'
1186
+ end
1187
+
1188
+ specify "should accept virtual row blocks" do
1189
+ @d.select_group{name}.sql.should == 'SELECT name FROM test GROUP BY name'
1190
+ @d.select_group{[name, f(v).as(a)]}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
1191
+ @d.select_group(:name){f(v).as(a)}.sql.should == 'SELECT name, f(v) AS a FROM test GROUP BY name, f(v)'
1192
+ end
1193
+ end
1194
+
1201
1195
  describe "Dataset#select_all" do
1202
1196
  before do
1203
1197
  @d = Sequel::Dataset.new(nil).from(:test)
@@ -1207,9 +1201,43 @@ describe "Dataset#select_all" do
1207
1201
  @d.select_all.sql.should == 'SELECT * FROM test'
1208
1202
  end
1209
1203
 
1210
- specify "should overrun the previous select option" do
1204
+ specify "should override the previous select option" do
1211
1205
  @d.select!(:a, :b, :c).select_all.sql.should == 'SELECT * FROM test'
1212
1206
  end
1207
+
1208
+ specify "should select all columns in a table if given an argument" do
1209
+ @d.select_all(:test).sql.should == 'SELECT test.* FROM test'
1210
+ end
1211
+
1212
+ specify "should select all columns all tables if given a multiple arguments" do
1213
+ @d.select_all(:test, :foo).sql.should == 'SELECT test.*, foo.* FROM test'
1214
+ end
1215
+
1216
+ specify "should work correctly with qualified symbols" do
1217
+ @d.select_all(:sch__test).sql.should == 'SELECT sch.test.* FROM test'
1218
+ end
1219
+
1220
+ specify "should work correctly with aliased symbols" do
1221
+ @d.select_all(:test___al).sql.should == 'SELECT al.* FROM test'
1222
+ @d.select_all(:sch__test___al).sql.should == 'SELECT al.* FROM test'
1223
+ end
1224
+
1225
+ specify "should work correctly with SQL::Identifiers" do
1226
+ @d.select_all(:test.identifier).sql.should == 'SELECT test.* FROM test'
1227
+ end
1228
+
1229
+ specify "should work correctly with SQL::QualifiedIdentifier" do
1230
+ @d.select_all(:test.qualify(:sch)).sql.should == 'SELECT sch.test.* FROM test'
1231
+ end
1232
+
1233
+ specify "should work correctly with SQL::AliasedExpressions" do
1234
+ @d.select_all(:test.as(:al)).sql.should == 'SELECT al.* FROM test'
1235
+ end
1236
+
1237
+ specify "should work correctly with SQL::JoinClauses" do
1238
+ d = @d.cross_join(:foo).cross_join(:test___al)
1239
+ @d.select_all(*d.opts[:join]).sql.should == 'SELECT foo.*, al.* FROM test'
1240
+ end
1213
1241
  end
1214
1242
 
1215
1243
  describe "Dataset#select_more" do
@@ -1254,6 +1282,14 @@ describe "Dataset#select_append" do
1254
1282
  @d.select(:a).select_append{|o| o.b}.sql.should == 'SELECT a, b FROM test'
1255
1283
  @d.select(:a.*).select_append(:b.*){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1256
1284
  end
1285
+
1286
+ specify "should select from all from and join tables if SELECT *, column not supported" do
1287
+ @d.meta_def(:supports_select_all_and_column?){false}
1288
+ @d.select_append(:b).sql.should == 'SELECT test.*, b FROM test'
1289
+ @d.from(:test, :c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test, c'
1290
+ @d.cross_join(:c).select_append(:b).sql.should == 'SELECT test.*, c.*, b FROM test CROSS JOIN c'
1291
+ @d.cross_join(:c).cross_join(:d).select_append(:b).sql.should == 'SELECT test.*, c.*, d.*, b FROM test CROSS JOIN c CROSS JOIN d'
1292
+ end
1257
1293
  end
1258
1294
 
1259
1295
  describe "Dataset#order" do
@@ -1262,38 +1298,31 @@ describe "Dataset#order" do
1262
1298
  end
1263
1299
 
1264
1300
  specify "should include an ORDER BY clause in the select statement" do
1265
- @dataset.order(:name).sql.should ==
1266
- 'SELECT * FROM test ORDER BY name'
1301
+ @dataset.order(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1267
1302
  end
1268
1303
 
1269
1304
  specify "should accept multiple arguments" do
1270
- @dataset.order(:name, :price.desc).sql.should ==
1271
- 'SELECT * FROM test ORDER BY name, price DESC'
1305
+ @dataset.order(:name, :price.desc).sql.should == 'SELECT * FROM test ORDER BY name, price DESC'
1272
1306
  end
1273
1307
 
1274
1308
  specify "should accept :nulls options for asc and desc" do
1275
- @dataset.order(:name.asc(:nulls=>:last), :price.desc(:nulls=>:first)).sql.should ==
1276
- 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
1309
+ @dataset.order(:name.asc(:nulls=>:last), :price.desc(:nulls=>:first)).sql.should == 'SELECT * FROM test ORDER BY name ASC NULLS LAST, price DESC NULLS FIRST'
1277
1310
  end
1278
1311
 
1279
- specify "should overrun a previous ordering" do
1280
- @dataset.order(:name).order(:stamp).sql.should ==
1281
- 'SELECT * FROM test ORDER BY stamp'
1312
+ specify "should override a previous ordering" do
1313
+ @dataset.order(:name).order(:stamp).sql.should == 'SELECT * FROM test ORDER BY stamp'
1282
1314
  end
1283
1315
 
1284
1316
  specify "should accept a literal string" do
1285
- @dataset.order('dada ASC'.lit).sql.should ==
1286
- 'SELECT * FROM test ORDER BY dada ASC'
1317
+ @dataset.order('dada ASC'.lit).sql.should == 'SELECT * FROM test ORDER BY dada ASC'
1287
1318
  end
1288
1319
 
1289
1320
  specify "should accept a hash as an expression" do
1290
- @dataset.order(:name=>nil).sql.should ==
1291
- 'SELECT * FROM test ORDER BY (name IS NULL)'
1321
+ @dataset.order(:name=>nil).sql.should == 'SELECT * FROM test ORDER BY (name IS NULL)'
1292
1322
  end
1293
1323
 
1294
1324
  specify "should accept a nil to remove ordering" do
1295
- @dataset.order(:bah).order(nil).sql.should ==
1296
- 'SELECT * FROM test'
1325
+ @dataset.order(:bah).order(nil).sql.should == 'SELECT * FROM test'
1297
1326
  end
1298
1327
 
1299
1328
  specify "should accept a block that yields a virtual row" do
@@ -1355,6 +1384,13 @@ describe "Dataset#with_sql" do
1355
1384
  specify "should keep row_proc" do
1356
1385
  @dataset.with_sql('SELECT 1 FROM test').row_proc.should == @dataset.row_proc
1357
1386
  end
1387
+
1388
+ specify "should work with method symbols and arguments" do
1389
+ @dataset.with_sql(:delete_sql).sql.should == 'DELETE FROM test'
1390
+ @dataset.with_sql(:insert_sql, :b=>1).sql.should == 'INSERT INTO test (b) VALUES (1)'
1391
+ @dataset.with_sql(:update_sql, :b=>1).sql.should == 'UPDATE test SET b = 1'
1392
+ end
1393
+
1358
1394
  end
1359
1395
 
1360
1396
  describe "Dataset#order_by" do
@@ -1363,28 +1399,23 @@ describe "Dataset#order_by" do
1363
1399
  end
1364
1400
 
1365
1401
  specify "should include an ORDER BY clause in the select statement" do
1366
- @dataset.order_by(:name).sql.should ==
1367
- 'SELECT * FROM test ORDER BY name'
1402
+ @dataset.order_by(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1368
1403
  end
1369
1404
 
1370
1405
  specify "should accept multiple arguments" do
1371
- @dataset.order_by(:name, :price.desc).sql.should ==
1372
- 'SELECT * FROM test ORDER BY name, price DESC'
1406
+ @dataset.order_by(:name, :price.desc).sql.should == 'SELECT * FROM test ORDER BY name, price DESC'
1373
1407
  end
1374
1408
 
1375
- specify "should overrun a previous ordering" do
1376
- @dataset.order_by(:name).order(:stamp).sql.should ==
1377
- 'SELECT * FROM test ORDER BY stamp'
1409
+ specify "should override a previous ordering" do
1410
+ @dataset.order_by(:name).order(:stamp).sql.should == 'SELECT * FROM test ORDER BY stamp'
1378
1411
  end
1379
1412
 
1380
1413
  specify "should accept a string" do
1381
- @dataset.order_by('dada ASC'.lit).sql.should ==
1382
- 'SELECT * FROM test ORDER BY dada ASC'
1414
+ @dataset.order_by('dada ASC'.lit).sql.should == 'SELECT * FROM test ORDER BY dada ASC'
1383
1415
  end
1384
1416
 
1385
1417
  specify "should accept a nil to remove ordering" do
1386
- @dataset.order_by(:bah).order_by(nil).sql.should ==
1387
- 'SELECT * FROM test'
1418
+ @dataset.order_by(:bah).order_by(nil).sql.should == 'SELECT * FROM test'
1388
1419
  end
1389
1420
  end
1390
1421
 
@@ -1417,13 +1448,11 @@ describe "Dataset#order_prepend" do
1417
1448
  end
1418
1449
 
1419
1450
  specify "should include an ORDER BY clause in the select statement" do
1420
- @dataset.order_prepend(:name).sql.should ==
1421
- 'SELECT * FROM test ORDER BY name'
1451
+ @dataset.order_prepend(:name).sql.should == 'SELECT * FROM test ORDER BY name'
1422
1452
  end
1423
1453
 
1424
1454
  specify "should add to the beginning of a previous ordering" do
1425
- @dataset.order(:name).order_prepend(:stamp.desc).sql.should ==
1426
- 'SELECT * FROM test ORDER BY stamp DESC, name'
1455
+ @dataset.order(:name).order_prepend(:stamp.desc).sql.should == 'SELECT * FROM test ORDER BY stamp DESC, name'
1427
1456
  end
1428
1457
 
1429
1458
  specify "should accept a block that yields a virtual row" do
@@ -1438,45 +1467,36 @@ describe "Dataset#reverse_order" do
1438
1467
  end
1439
1468
 
1440
1469
  specify "should use DESC as default order" do
1441
- @dataset.reverse_order(:name).sql.should ==
1442
- 'SELECT * FROM test ORDER BY name DESC'
1470
+ @dataset.reverse_order(:name).sql.should == 'SELECT * FROM test ORDER BY name DESC'
1443
1471
  end
1444
1472
 
1445
1473
  specify "should invert the order given" do
1446
- @dataset.reverse_order(:name.desc).sql.should ==
1447
- 'SELECT * FROM test ORDER BY name ASC'
1474
+ @dataset.reverse_order(:name.desc).sql.should == 'SELECT * FROM test ORDER BY name ASC'
1448
1475
  end
1449
1476
 
1450
1477
  specify "should invert the order for ASC expressions" do
1451
- @dataset.reverse_order(:name.asc).sql.should ==
1452
- 'SELECT * FROM test ORDER BY name DESC'
1478
+ @dataset.reverse_order(:name.asc).sql.should == 'SELECT * FROM test ORDER BY name DESC'
1453
1479
  end
1454
1480
 
1455
1481
  specify "should accept multiple arguments" do
1456
- @dataset.reverse_order(:name, :price.desc).sql.should ==
1457
- 'SELECT * FROM test ORDER BY name DESC, price ASC'
1482
+ @dataset.reverse_order(:name, :price.desc).sql.should == 'SELECT * FROM test ORDER BY name DESC, price ASC'
1458
1483
  end
1459
1484
 
1460
1485
  specify "should handles NULLS ordering correctly when reversing" do
1461
- @dataset.reverse_order(:name.asc(:nulls=>:first), :price.desc(:nulls=>:last)).sql.should ==
1462
- 'SELECT * FROM test ORDER BY name DESC NULLS LAST, price ASC NULLS FIRST'
1486
+ @dataset.reverse_order(:name.asc(:nulls=>:first), :price.desc(:nulls=>:last)).sql.should == 'SELECT * FROM test ORDER BY name DESC NULLS LAST, price ASC NULLS FIRST'
1463
1487
  end
1464
1488
 
1465
1489
  specify "should reverse a previous ordering if no arguments are given" do
1466
- @dataset.order(:name).reverse_order.sql.should ==
1467
- 'SELECT * FROM test ORDER BY name DESC'
1468
- @dataset.order(:clumsy.desc, :fool).reverse_order.sql.should ==
1469
- 'SELECT * FROM test ORDER BY clumsy ASC, fool DESC'
1490
+ @dataset.order(:name).reverse_order.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1491
+ @dataset.order(:clumsy.desc, :fool).reverse_order.sql.should == 'SELECT * FROM test ORDER BY clumsy ASC, fool DESC'
1470
1492
  end
1471
1493
 
1472
1494
  specify "should return an unordered dataset for a dataset with no order" do
1473
- @dataset.unordered.reverse_order.sql.should ==
1474
- 'SELECT * FROM test'
1495
+ @dataset.unordered.reverse_order.sql.should == 'SELECT * FROM test'
1475
1496
  end
1476
1497
 
1477
1498
  specify "should have #reverse alias" do
1478
- @dataset.order(:name).reverse.sql.should ==
1479
- 'SELECT * FROM test ORDER BY name DESC'
1499
+ @dataset.order(:name).reverse.sql.should == 'SELECT * FROM test ORDER BY name DESC'
1480
1500
  end
1481
1501
  end
1482
1502
 
@@ -1486,42 +1506,33 @@ describe "Dataset#limit" do
1486
1506
  end
1487
1507
 
1488
1508
  specify "should include a LIMIT clause in the select statement" do
1489
- @dataset.limit(10).sql.should ==
1490
- 'SELECT * FROM test LIMIT 10'
1509
+ @dataset.limit(10).sql.should == 'SELECT * FROM test LIMIT 10'
1491
1510
  end
1492
1511
 
1493
1512
  specify "should accept ranges" do
1494
- @dataset.limit(3..7).sql.should ==
1495
- 'SELECT * FROM test LIMIT 5 OFFSET 3'
1496
-
1497
- @dataset.limit(3...7).sql.should ==
1498
- 'SELECT * FROM test LIMIT 4 OFFSET 3'
1513
+ @dataset.limit(3..7).sql.should == 'SELECT * FROM test LIMIT 5 OFFSET 3'
1514
+ @dataset.limit(3...7).sql.should == 'SELECT * FROM test LIMIT 4 OFFSET 3'
1499
1515
  end
1500
1516
 
1501
1517
  specify "should include an offset if a second argument is given" do
1502
- @dataset.limit(6, 10).sql.should ==
1503
- 'SELECT * FROM test LIMIT 6 OFFSET 10'
1504
- end
1518
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 10'
1519
+ end
1505
1520
 
1506
1521
  specify "should convert regular strings to integers" do
1507
- @dataset.limit('6', 'a() - 1').sql.should ==
1508
- 'SELECT * FROM test LIMIT 6 OFFSET 0'
1522
+ @dataset.limit('6', 'a() - 1').sql.should == 'SELECT * FROM test LIMIT 6 OFFSET 0'
1509
1523
  end
1510
1524
 
1511
1525
  specify "should not convert literal strings to integers" do
1512
- @dataset.limit('6'.lit, 'a() - 1'.lit).sql.should ==
1513
- 'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
1526
+ @dataset.limit('6'.lit, 'a() - 1'.lit).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET a() - 1'
1514
1527
  end
1515
1528
 
1516
1529
  specify "should not convert other objects" do
1517
- @dataset.limit(6, :a.sql_function - 1).sql.should ==
1518
- 'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
1530
+ @dataset.limit(6, :a.sql_function - 1).sql.should == 'SELECT * FROM test LIMIT 6 OFFSET (a() - 1)'
1519
1531
  end
1520
1532
 
1521
1533
  specify "should work with fixed sql datasets" do
1522
1534
  @dataset.opts[:sql] = 'select * from cccc'
1523
- @dataset.limit(6, 10).sql.should ==
1524
- 'SELECT * FROM (select * from cccc) AS t1 LIMIT 6 OFFSET 10'
1535
+ @dataset.limit(6, 10).sql.should == 'SELECT * FROM (select * from cccc) AS t1 LIMIT 6 OFFSET 10'
1525
1536
  end
1526
1537
 
1527
1538
  specify "should raise an error if an invalid limit or offset is used" do
@@ -1535,15 +1546,10 @@ describe "Dataset#limit" do
1535
1546
  end
1536
1547
 
1537
1548
  describe "Dataset#naked" do
1538
- before do
1539
- @d1 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4})
1540
- @d2 = @d1.clone
1541
- @d2.row_proc = Proc.new{|r| r}
1542
- end
1543
-
1544
1549
  specify "should remove any existing row_proc" do
1545
- naked = @d2.naked
1546
- naked.row_proc.should be_nil
1550
+ d = Sequel::Dataset.new(nil)
1551
+ d.row_proc = Proc.new{|r| r}
1552
+ d.naked.row_proc.should be_nil
1547
1553
  end
1548
1554
  end
1549
1555
 
@@ -1571,38 +1577,42 @@ describe "Dataset#qualified_column_name" do
1571
1577
  end
1572
1578
  end
1573
1579
 
1574
- class DummyDataset < Sequel::Dataset
1575
- VALUES = [
1576
- {:a => 1, :b => 2},
1577
- {:a => 3, :b => 4},
1578
- {:a => 5, :b => 6}
1579
- ]
1580
- def fetch_rows(sql, &block)
1581
- VALUES.each(&block)
1582
- end
1583
- end
1584
-
1585
1580
  describe "Dataset#map" do
1586
1581
  before do
1587
- @d = DummyDataset.new(nil).from(:items)
1582
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1588
1583
  end
1589
1584
 
1590
1585
  specify "should provide the usual functionality if no argument is given" do
1591
- @d.map {|n| n[:a] + n[:b]}.should == [3, 7, 11]
1586
+ @d.map{|n| n[:a] + n[:b]}.should == [3, 7, 11]
1592
1587
  end
1593
1588
 
1594
1589
  specify "should map using #[column name] if column name is given" do
1595
1590
  @d.map(:a).should == [1, 3, 5]
1596
1591
  end
1597
1592
 
1593
+ specify "should support multiple column names if an array of column names is given" do
1594
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1595
+ end
1596
+
1597
+ specify "should not call the row_proc if an argument is given" do
1598
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1599
+ @d.map(:a).should == [1, 3, 5]
1600
+ @d.map([:a, :b]).should == [[1, 2], [3, 4], [5, 6]]
1601
+ end
1602
+
1603
+ specify "should call the row_proc if no argument is given" do
1604
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1605
+ @d.map{|n| n[:a] + n[:b]}.should == [6, 14, 22]
1606
+ end
1607
+
1598
1608
  specify "should return the complete dataset values if nothing is given" do
1599
- @d.map.to_a.should == DummyDataset::VALUES
1609
+ @d.map.to_a.should == [{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}]
1600
1610
  end
1601
1611
  end
1602
1612
 
1603
1613
  describe "Dataset#to_hash" do
1604
1614
  before do
1605
- @d = DummyDataset.new(nil).from(:items)
1615
+ @d = Sequel.mock(:fetch=>[{:a => 1, :b => 2}, {:a => 3, :b => 4}, {:a => 5, :b => 6}])[:items]
1606
1616
  end
1607
1617
 
1608
1618
  specify "should provide a hash with the first column as key and the second as value" do
@@ -1614,11 +1624,35 @@ describe "Dataset#to_hash" do
1614
1624
  @d.to_hash(:a).should == {1 => {:a => 1, :b => 2}, 3 => {:a => 3, :b => 4}, 5 => {:a => 5, :b => 6}}
1615
1625
  @d.to_hash(:b).should == {2 => {:a => 1, :b => 2}, 4 => {:a => 3, :b => 4}, 6 => {:a => 5, :b => 6}}
1616
1626
  end
1627
+
1628
+ specify "should support using an array of columns as either the key or the value" do
1629
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1630
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1631
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1632
+ @d.to_hash([:a, :b]).should == {[1, 2] => {:a => 1, :b => 2}, [3, 4] => {:a => 3, :b => 4}, [5, 6] => {:a => 5, :b => 6}}
1633
+ end
1634
+
1635
+ specify "should not call the row_proc if two arguments are given" do
1636
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1637
+ @d.to_hash(:a, :b).should == {1 => 2, 3 => 4, 5 => 6}
1638
+ @d.to_hash(:b, :a).should == {2 => 1, 4 => 3, 6 => 5}
1639
+ @d.to_hash([:a, :b], :b).should == {[1, 2] => 2, [3, 4] => 4, [5, 6] => 6}
1640
+ @d.to_hash(:b, [:a, :b]).should == {2 => [1, 2], 4 => [3, 4], 6 => [5, 6]}
1641
+ @d.to_hash([:b, :a], [:a, :b]).should == {[2, 1] => [1, 2], [4, 3] => [3, 4], [6, 5] => [5, 6]}
1642
+ end
1643
+
1644
+ specify "should call the row_proc if only a single argument is given" do
1645
+ @d.row_proc = proc{|r| h = {}; r.keys.each{|k| h[k] = r[k] * 2}; h}
1646
+ @d.to_hash(:a).should == {2 => {:a => 2, :b => 4}, 6 => {:a => 6, :b => 8}, 10 => {:a => 10, :b => 12}}
1647
+ @d.to_hash(:b).should == {4 => {:a => 2, :b => 4}, 8 => {:a => 6, :b => 8}, 12 => {:a => 10, :b => 12}}
1648
+ @d.to_hash([:a, :b]).should == {[2, 4] => {:a => 2, :b => 4}, [6, 8] => {:a => 6, :b => 8}, [10, 12] => {:a => 10, :b => 12}}
1649
+ end
1650
+
1617
1651
  end
1618
1652
 
1619
1653
  describe "Dataset#distinct" do
1620
1654
  before do
1621
- @db = MockDatabase.new
1655
+ @db = Sequel.mock
1622
1656
  @dataset = @db[:test].select(:name)
1623
1657
  end
1624
1658
 
@@ -1645,132 +1679,100 @@ end
1645
1679
 
1646
1680
  describe "Dataset#count" do
1647
1681
  before do
1648
- @c = Class.new(Sequel::Dataset) do
1649
- def self.sql
1650
- @@sql
1651
- end
1652
-
1653
- def fetch_rows(sql)
1654
- @columns = [sql =~ /SELECT COUNT/i ? :count : :a]
1655
- @@sql = sql
1656
- yield({@columns.first=>1})
1657
- end
1658
- end
1659
- @dataset = @c.new(nil).from(:test)
1682
+ @db = Sequel.mock(:fetch=>{:count=>1})
1683
+ @dataset = @db.from(:test).columns(:count)
1660
1684
  end
1661
1685
 
1662
1686
  specify "should format SQL properly" do
1663
1687
  @dataset.count.should == 1
1664
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test LIMIT 1'
1688
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LIMIT 1']
1665
1689
  end
1666
1690
 
1667
1691
  specify "should include the where clause if it's there" do
1668
1692
  @dataset.filter(:abc.sql_number < 30).count.should == 1
1669
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1'
1693
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test WHERE (abc < 30) LIMIT 1']
1670
1694
  end
1671
1695
 
1672
1696
  specify "should count properly for datasets with fixed sql" do
1673
1697
  @dataset.opts[:sql] = "select abc from xyz"
1674
1698
  @dataset.count.should == 1
1675
- @c.sql.should == "SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"
1699
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (select abc from xyz) AS t1 LIMIT 1"]
1676
1700
  end
1677
1701
 
1678
1702
  specify "should count properly when using UNION, INTERSECT, or EXCEPT" do
1679
1703
  @dataset.union(@dataset).count.should == 1
1680
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 1"
1704
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 1"]
1681
1705
  @dataset.intersect(@dataset).count.should == 1
1682
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test INTERSECT SELECT * FROM test) AS t1 LIMIT 1"
1706
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test INTERSECT SELECT * FROM test) AS t1 LIMIT 1"]
1683
1707
  @dataset.except(@dataset).count.should == 1
1684
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test EXCEPT SELECT * FROM test) AS t1 LIMIT 1"
1708
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test EXCEPT SELECT * FROM test) AS t1 LIMIT 1"]
1685
1709
  end
1686
1710
 
1687
1711
  specify "should return limit if count is greater than it" do
1688
1712
  @dataset.limit(5).count.should == 1
1689
- @c.sql.should == "SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"
1713
+ @db.sqls.should == ["SELECT COUNT(*) AS count FROM (SELECT * FROM test LIMIT 5) AS t1 LIMIT 1"]
1690
1714
  end
1691
1715
 
1692
1716
  it "should work on a graphed_dataset" do
1693
1717
  @dataset.should_receive(:columns).twice.and_return([:a])
1694
1718
  @dataset.graph(@dataset, [:a], :table_alias=>:test2).count.should == 1
1695
- @c.sql.should == 'SELECT COUNT(*) AS count FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1'
1719
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
1696
1720
  end
1697
1721
 
1698
1722
  specify "should not cache the columns value" do
1699
- ds = @dataset.from(:blah)
1723
+ ds = @dataset.from(:blah).columns(:a)
1700
1724
  ds.columns.should == [:a]
1701
1725
  ds.count.should == 1
1702
- @c.sql.should == 'SELECT COUNT(*) AS count FROM blah LIMIT 1'
1726
+ @db.sqls.should == ['SELECT COUNT(*) AS count FROM blah LIMIT 1']
1703
1727
  ds.columns.should == [:a]
1704
1728
  end
1705
1729
  end
1706
1730
 
1707
-
1708
1731
  describe "Dataset#group_and_count" do
1709
1732
  before do
1710
- @c = Class.new(Sequel::Dataset) do
1711
- def self.sql
1712
- @@sql
1713
- end
1714
-
1715
- def fetch_rows(sql)
1716
- @@sql = sql
1717
- yield({1 => 1})
1718
- end
1719
- end
1720
- @ds = @c.new(nil).from(:test)
1733
+ @ds = Sequel::Dataset.new(nil).from(:test)
1721
1734
  end
1722
1735
 
1723
1736
  specify "should format SQL properly" do
1724
- @ds.group_and_count(:name).sql.should ==
1725
- "SELECT name, count(*) AS count FROM test GROUP BY name"
1737
+ @ds.group_and_count(:name).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1726
1738
  end
1727
1739
 
1728
1740
  specify "should accept multiple columns for grouping" do
1729
- @ds.group_and_count(:a, :b).sql.should ==
1730
- "SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
1741
+ @ds.group_and_count(:a, :b).sql.should == "SELECT a, b, count(*) AS count FROM test GROUP BY a, b"
1731
1742
  end
1732
1743
 
1733
1744
  specify "should format column aliases in the select clause but not in the group clause" do
1734
- @ds.group_and_count(:name___n).sql.should ==
1735
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1736
- @ds.group_and_count(:name__n).sql.should ==
1737
- "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
1745
+ @ds.group_and_count(:name___n).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1746
+ @ds.group_and_count(:name__n).sql.should == "SELECT name.n, count(*) AS count FROM test GROUP BY name.n"
1738
1747
  end
1739
1748
 
1740
1749
  specify "should handle identifiers" do
1741
- @ds.group_and_count(:name___n.identifier).sql.should ==
1742
- "SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
1750
+ @ds.group_and_count(:name___n.identifier).sql.should == "SELECT name___n, count(*) AS count FROM test GROUP BY name___n"
1743
1751
  end
1744
1752
 
1745
1753
  specify "should handle literal strings" do
1746
- @ds.group_and_count("name".lit).sql.should ==
1747
- "SELECT name, count(*) AS count FROM test GROUP BY name"
1754
+ @ds.group_and_count("name".lit).sql.should == "SELECT name, count(*) AS count FROM test GROUP BY name"
1748
1755
  end
1749
1756
 
1750
1757
  specify "should handle aliased expressions" do
1751
- @ds.group_and_count(:name.as(:n)).sql.should ==
1752
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1753
- @ds.group_and_count(:name.identifier.as(:n)).sql.should ==
1754
- "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1758
+ @ds.group_and_count(:name.as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1759
+ @ds.group_and_count(:name.identifier.as(:n)).sql.should == "SELECT name AS n, count(*) AS count FROM test GROUP BY name"
1760
+ end
1761
+
1762
+ specify "should take a virtual row block" do
1763
+ @ds.group_and_count{(type_id > 1).as(t)}.sql.should == "SELECT (type_id > 1) AS t, count(*) AS count FROM test GROUP BY (type_id > 1)"
1764
+ @ds.group_and_count{[(type_id > 1).as(t), type_id < 2]}.sql.should == "SELECT (type_id > 1) AS t, (type_id < 2), count(*) AS count FROM test GROUP BY (type_id > 1), (type_id < 2)"
1765
+ @ds.group_and_count(:foo){type_id > 1}.sql.should == "SELECT foo, (type_id > 1), count(*) AS count FROM test GROUP BY foo, (type_id > 1)"
1755
1766
  end
1756
1767
  end
1757
1768
 
1758
1769
  describe "Dataset#empty?" do
1759
1770
  specify "should return true if records exist in the dataset" do
1760
- @c = Class.new(Sequel::Dataset) do
1761
- def self.sql
1762
- @@sql
1763
- end
1764
-
1765
- def fetch_rows(sql)
1766
- @@sql = sql
1767
- yield({1 => 1}) unless sql =~ /WHERE 'f'/
1768
- end
1769
- end
1770
- @c.new(nil).from(:test).should_not be_empty
1771
- @c.sql.should == 'SELECT 1 FROM test LIMIT 1'
1772
- @c.new(nil).from(:test).filter(false).should be_empty
1773
- @c.sql.should == "SELECT 1 FROM test WHERE 'f' LIMIT 1"
1771
+ db = Sequel.mock(:fetch=>proc{|sql| {1=>1} unless sql =~ /WHERE 'f'/})
1772
+ db.from(:test).should_not be_empty
1773
+ db.sqls.should == ['SELECT 1 FROM test LIMIT 1']
1774
+ db.from(:test).filter(false).should be_empty
1775
+ db.sqls.should == ["SELECT 1 FROM test WHERE 'f' LIMIT 1"]
1774
1776
  end
1775
1777
  end
1776
1778
 
@@ -1849,25 +1851,31 @@ describe "Dataset#from_self" do
1849
1851
  'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name INNER JOIN posts ON (posts.alias = some_name.name)'
1850
1852
  end
1851
1853
 
1852
- specify "should not options such as server" do
1854
+ specify "should not remove non-SQL options such as :server" do
1853
1855
  @ds.server(:blah).from_self(:alias=>:some_name).opts[:server].should == :blah
1854
1856
  end
1855
1857
 
1858
+ specify "should hoist WITH clauses in current dataset if dataset doesn't support WITH in subselect" do
1859
+ ds = Sequel::Dataset.new(nil)
1860
+ ds.meta_def(:supports_cte?){true}
1861
+ ds.meta_def(:supports_cte_in_subselect?){false}
1862
+ ds.from(:a).with(:a, ds.from(:b)).from_self.sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
1863
+ ds.from(:a, :c).with(:a, ds.from(:b)).with(:c, ds.from(:d)).from_self.sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a, c) AS t1'
1864
+ end
1856
1865
  end
1857
1866
 
1858
1867
  describe "Dataset#join_table" do
1859
1868
  before do
1860
- @d = MockDataset.new(nil).from(:items)
1869
+ @d = Sequel::Dataset.new(nil).from(:items)
1861
1870
  @d.quote_identifiers = true
1862
1871
  end
1863
1872
 
1864
1873
  specify "should format the JOIN clause properly" do
1865
- @d.join_table(:left_outer, :categories, :category_id => :id).sql.should ==
1866
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1874
+ @d.join_table(:left_outer, :categories, :category_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1867
1875
  end
1868
1876
 
1869
1877
  specify "should handle multiple conditions on the same join table column" do
1870
- @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1878
+ @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1871
1879
  'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND ("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100))'
1872
1880
  end
1873
1881
 
@@ -1877,45 +1885,30 @@ describe "Dataset#join_table" do
1877
1885
  end
1878
1886
 
1879
1887
  specify "should include ORDER BY clause if applicable" do
1880
- @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should ==
1881
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
1888
+ @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should == 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
1882
1889
  end
1883
1890
 
1884
1891
  specify "should support multiple joins" do
1885
- @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.should ==
1886
- 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1892
+ @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.should == 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1887
1893
  end
1888
1894
 
1889
1895
  specify "should support arbitrary join types" do
1890
- @d.join_table(:magic, :categories, :category_id=>:id).sql.should ==
1891
- 'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1896
+ @d.join_table(:magic, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1892
1897
  end
1893
1898
 
1894
1899
  specify "should support many join methods" do
1895
- @d.left_outer_join(:categories, :category_id=>:id).sql.should ==
1896
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1897
- @d.right_outer_join(:categories, :category_id=>:id).sql.should ==
1898
- 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1899
- @d.full_outer_join(:categories, :category_id=>:id).sql.should ==
1900
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1901
- @d.inner_join(:categories, :category_id=>:id).sql.should ==
1902
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1903
- @d.left_join(:categories, :category_id=>:id).sql.should ==
1904
- 'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1905
- @d.right_join(:categories, :category_id=>:id).sql.should ==
1906
- 'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1907
- @d.full_join(:categories, :category_id=>:id).sql.should ==
1908
- 'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1909
- @d.natural_join(:categories).sql.should ==
1910
- 'SELECT * FROM "items" NATURAL JOIN "categories"'
1911
- @d.natural_left_join(:categories).sql.should ==
1912
- 'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
1913
- @d.natural_right_join(:categories).sql.should ==
1914
- 'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
1915
- @d.natural_full_join(:categories).sql.should ==
1916
- 'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
1917
- @d.cross_join(:categories).sql.should ==
1918
- 'SELECT * FROM "items" CROSS JOIN "categories"'
1900
+ @d.left_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1901
+ @d.right_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1902
+ @d.full_outer_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1903
+ @d.inner_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1904
+ @d.left_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1905
+ @d.right_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1906
+ @d.full_join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1907
+ @d.natural_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
1908
+ @d.natural_left_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
1909
+ @d.natural_right_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
1910
+ @d.natural_full_join(:categories).sql.should == 'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
1911
+ @d.cross_join(:categories).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
1919
1912
  end
1920
1913
 
1921
1914
  specify "should raise an error if additional arguments are provided to join methods that don't take conditions" do
@@ -1935,95 +1928,68 @@ describe "Dataset#join_table" do
1935
1928
  end
1936
1929
 
1937
1930
  specify "should default to a plain join if nil is used for the type" do
1938
- @d.join_table(nil, :categories, :category_id=>:id).sql.should ==
1939
- 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1931
+ @d.join_table(nil, :categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1940
1932
  end
1941
1933
 
1942
1934
  specify "should use an inner join for Dataset#join" do
1943
- @d.join(:categories, :category_id=>:id).sql.should ==
1944
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1935
+ @d.join(:categories, :category_id=>:id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1945
1936
  end
1946
1937
 
1947
1938
  specify "should support aliased tables using the deprecated argument" do
1948
- @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should ==
1949
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1939
+ @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1950
1940
  end
1951
1941
 
1952
1942
  specify "should support aliased tables using the :table_alias option" do
1953
- @d.from('stats').join('players', {:id => :player_id}, :table_alias=>:p).sql.should ==
1954
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1943
+ @d.from('stats').join('players', {:id => :player_id}, :table_alias=>:p).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1955
1944
  end
1956
1945
 
1957
1946
  specify "should support aliased tables using an implicit alias" do
1958
- @d.from('stats').join(:players.as(:p), {:id => :player_id}).sql.should ==
1959
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1947
+ @d.from('stats').join(:players.as(:p), {:id => :player_id}).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1960
1948
  end
1961
1949
 
1962
1950
  specify "should support using an alias for the FROM when doing the first join with unqualified condition columns" do
1963
- ds = MockDataset.new(nil).from(:foo => :f)
1964
- ds.quote_identifiers = true
1965
- ds.join_table(:inner, :bar, :id => :bar_id).sql.should ==
1966
- 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
1951
+ @d.from(:foo=>:f).join_table(:inner, :bar, :id => :bar_id).sql.should == 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
1967
1952
  end
1968
1953
 
1969
1954
  specify "should support implicit schemas in from table symbols" do
1970
- @d.from(:s__t).join(:u__v, {:id => :player_id}).sql.should ==
1971
- 'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
1955
+ @d.from(:s__t).join(:u__v, {:id => :player_id}).sql.should == 'SELECT * FROM "s"."t" INNER JOIN "u"."v" ON ("u"."v"."id" = "s"."t"."player_id")'
1972
1956
  end
1973
1957
 
1974
1958
  specify "should support implicit aliases in from table symbols" do
1975
- @d.from(:t___z).join(:v___y, {:id => :player_id}).sql.should ==
1976
- 'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
1977
- @d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.should ==
1978
- 'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
1959
+ @d.from(:t___z).join(:v___y, {:id => :player_id}).sql.should == 'SELECT * FROM "t" AS "z" INNER JOIN "v" AS "y" ON ("y"."id" = "z"."player_id")'
1960
+ @d.from(:s__t___z).join(:u__v___y, {:id => :player_id}).sql.should == 'SELECT * FROM "s"."t" AS "z" INNER JOIN "u"."v" AS "y" ON ("y"."id" = "z"."player_id")'
1979
1961
  end
1980
1962
 
1981
1963
  specify "should support AliasedExpressions" do
1982
- @d.from(:s.as(:t)).join(:u.as(:v), {:id => :player_id}).sql.should ==
1983
- 'SELECT * FROM "s" AS "t" INNER JOIN "u" AS "v" ON ("v"."id" = "t"."player_id")'
1964
+ @d.from(:s.as(:t)).join(:u.as(:v), {:id => :player_id}).sql.should == 'SELECT * FROM "s" AS "t" INNER JOIN "u" AS "v" ON ("v"."id" = "t"."player_id")'
1984
1965
  end
1985
1966
 
1986
1967
  specify "should support the :implicit_qualifier option" do
1987
- @d.from('stats').join('players', {:id => :player_id}, :implicit_qualifier=>:p).sql.should ==
1988
- 'SELECT * FROM "stats" INNER JOIN "players" ON ("players"."id" = "p"."player_id")'
1968
+ @d.from('stats').join('players', {:id => :player_id}, :implicit_qualifier=>:p).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" ON ("players"."id" = "p"."player_id")'
1989
1969
  end
1990
1970
 
1991
1971
  specify "should allow for arbitrary conditions in the JOIN clause" do
1992
- @d.join_table(:left_outer, :categories, :status => 0).sql.should ==
1993
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
1994
- @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
1995
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
1996
- @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1997
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
1998
- @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
1999
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
1972
+ @d.join_table(:left_outer, :categories, :status => 0).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
1973
+ @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
1974
+ @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
1975
+ @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
2000
1976
  end
2001
1977
 
2002
1978
  specify "should raise error for a table without a source" do
2003
- proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}. \
2004
- should raise_error(Sequel::Error)
1979
+ proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}.should raise_error(Sequel::Error)
2005
1980
  end
2006
1981
 
2007
1982
  specify "should support joining datasets" do
2008
1983
  ds = Sequel::Dataset.new(nil).from(:categories)
2009
-
2010
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2011
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id")'
2012
-
1984
+ @d.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id")'
2013
1985
  ds.filter!(:active => true)
2014
-
2015
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2016
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t1" ON ("t1"."item_id" = "items"."id")'
2017
-
2018
- @d.from_self.join_table(:left_outer, ds, :item_id => :id).sql.should ==
2019
- 'SELECT * FROM (SELECT * FROM "items") AS "t1" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t2" ON ("t2"."item_id" = "t1"."id")'
1986
+ @d.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t1" ON ("t1"."item_id" = "items"."id")'
1987
+ @d.from_self.join_table(:left_outer, ds, :item_id => :id).sql.should == 'SELECT * FROM (SELECT * FROM "items") AS "t1" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active IS TRUE)) AS "t2" ON ("t2"."item_id" = "t1"."id")'
2020
1988
  end
2021
1989
 
2022
1990
  specify "should support joining datasets and aliasing the join" do
2023
1991
  ds = Sequel::Dataset.new(nil).from(:categories)
2024
-
2025
- @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :ds).sql.should ==
2026
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
1992
+ @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :ds).sql.should == 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
2027
1993
  end
2028
1994
 
2029
1995
  specify "should support joining multiple datasets" do
@@ -2040,40 +2006,30 @@ describe "Dataset#join_table" do
2040
2006
  specify "should support joining objects that respond to :table_name" do
2041
2007
  ds = Object.new
2042
2008
  def ds.table_name; :categories end
2043
-
2044
- @d.join(ds, :item_id => :id).sql.should ==
2045
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
2009
+ @d.join(ds, :item_id => :id).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
2046
2010
  end
2047
2011
 
2048
- specify "should support using a SQL String as the join condition" do
2049
- @d.join(:categories, %{c.item_id = items.id}, :c).sql.should ==
2050
- 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2012
+ specify "should support using an SQL String as the join condition" do
2013
+ @d.join(:categories, "c.item_id = items.id", :c).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
2051
2014
  end
2052
2015
 
2053
2016
  specify "should support using a boolean column as the join condition" do
2054
- @d.join(:categories, :active).sql.should ==
2055
- 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2017
+ @d.join(:categories, :active).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
2056
2018
  end
2057
2019
 
2058
2020
  specify "should support using an expression as the join condition" do
2059
- @d.join(:categories, :number.sql_number > 10).sql.should ==
2060
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
2021
+ @d.join(:categories, :number.sql_number > 10).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
2061
2022
  end
2062
2023
 
2063
- specify "should support natural and cross joins using nil" do
2064
- @d.join_table(:natural, :categories).sql.should ==
2065
- 'SELECT * FROM "items" NATURAL JOIN "categories"'
2066
- @d.join_table(:cross, :categories, nil).sql.should ==
2067
- 'SELECT * FROM "items" CROSS JOIN "categories"'
2068
- @d.join_table(:natural, :categories, nil, :c).sql.should ==
2069
- 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
2024
+ specify "should support natural and cross joins" do
2025
+ @d.join_table(:natural, :categories).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories"'
2026
+ @d.join_table(:cross, :categories, nil).sql.should == 'SELECT * FROM "items" CROSS JOIN "categories"'
2027
+ @d.join_table(:natural, :categories, nil, :c).sql.should == 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
2070
2028
  end
2071
2029
 
2072
2030
  specify "should support joins with a USING clause if an array of symbols is used" do
2073
- @d.join(:categories, [:id]).sql.should ==
2074
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
2075
- @d.join(:categories, [:id1, :id2]).sql.should ==
2076
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
2031
+ @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
2032
+ @d.join(:categories, [:id1, :id2]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
2077
2033
  end
2078
2034
 
2079
2035
  specify "should emulate JOIN USING (poorly) if the dataset doesn't support it" do
@@ -2081,6 +2037,12 @@ describe "Dataset#join_table" do
2081
2037
  @d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
2082
2038
  end
2083
2039
 
2040
+ specify "should hoist WITH clauses from subqueries if the dataset doesn't support CTEs in subselects" do
2041
+ @d.meta_def(:supports_cte?){true}
2042
+ @d.meta_def(:supports_cte_in_subselect?){false}
2043
+ @d.join(Sequel::Dataset.new(nil).from(:categories).with(:a, Sequel::Dataset.new(nil).from(:b)), [:id]).sql.should == 'WITH "a" AS (SELECT * FROM b) SELECT * FROM "items" INNER JOIN (SELECT * FROM categories) AS "t1" USING ("id")'
2044
+ end
2045
+
2084
2046
  specify "should raise an error if using an array of symbols with a block" do
2085
2047
  proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
2086
2048
  end
@@ -2135,27 +2097,21 @@ describe "Dataset#join_table" do
2135
2097
  end
2136
2098
 
2137
2099
  specify "should use the block result as the only condition if no condition is given" do
2138
- @d.join(:categories){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
2139
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" = "items"."c")'
2140
- @d.join(:categories){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
2141
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" > "items"."c")'
2100
+ @d.join(:categories){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" = "items"."c")'
2101
+ @d.join(:categories){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" > "items"."c")'
2142
2102
  end
2143
2103
 
2144
2104
  specify "should combine the block conditions and argument conditions if both given" do
2145
- @d.join(:categories, :a=>:d){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
2146
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" = "items"."c"))'
2147
- @d.join(:categories, :a=>:d){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
2148
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" > "items"."c"))'
2105
+ @d.join(:categories, :a=>:d){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" = "items"."c"))'
2106
+ @d.join(:categories, :a=>:d){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" > "items"."c"))'
2149
2107
  end
2150
2108
 
2151
2109
  specify "should prefer explicit aliases over implicit" do
2152
- @d.from(:items___i).join(:categories___c, {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should ==
2153
- 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2110
+ @d.from(:items___i).join(:categories___c, {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should == 'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2154
2111
  @d.from(:items.as(:i)).join(:categories.as(:c), {:category_id => :id}, {:table_alias=>:c2, :implicit_qualifier=>:i2}).sql.should ==
2155
2112
  'SELECT * FROM "items" AS "i" INNER JOIN "categories" AS "c2" ON ("c2"."category_id" = "i2"."id")'
2156
2113
  end
2157
2114
 
2158
-
2159
2115
  specify "should not allow insert, update, delete, or truncate" do
2160
2116
  proc{@d.join(:categories, :a=>:d).insert_sql}.should raise_error(Sequel::InvalidOperation)
2161
2117
  proc{@d.join(:categories, :a=>:d).update_sql(:a=>1)}.should raise_error(Sequel::InvalidOperation)
@@ -2170,81 +2126,48 @@ describe "Dataset#join_table" do
2170
2126
  end
2171
2127
 
2172
2128
  describe "Dataset#[]=" do
2173
- before do
2174
- c = Class.new(Sequel::Dataset) do
2175
- def last_sql
2176
- @@last_sql
2177
- end
2178
-
2179
- def update(*args)
2180
- @@last_sql = update_sql(*args)
2181
- end
2182
- end
2183
-
2184
- @d = c.new(nil).from(:items)
2185
- end
2186
-
2187
2129
  specify "should perform an update on the specified filter" do
2188
- @d[:a => 1] = {:x => 3}
2189
- @d.last_sql.should == 'UPDATE items SET x = 3 WHERE (a = 1)'
2130
+ db = Sequel.mock
2131
+ ds = db[:items]
2132
+ ds[:a => 1] = {:x => 3}
2133
+ db.sqls.should == ['UPDATE items SET x = 3 WHERE (a = 1)']
2190
2134
  end
2191
2135
  end
2192
2136
 
2193
2137
  describe "Dataset#set" do
2194
- before do
2195
- c = Class.new(Sequel::Dataset) do
2196
- def last_sql
2197
- @@last_sql
2198
- end
2199
-
2200
- def update(*args, &block)
2201
- @@last_sql = update_sql(*args, &block)
2202
- end
2203
- end
2204
-
2205
- @d = c.new(nil).from(:items)
2206
- end
2207
-
2208
2138
  specify "should act as alias to #update" do
2209
- @d.set({:x => 3})
2210
- @d.last_sql.should == 'UPDATE items SET x = 3'
2139
+ db = Sequel.mock
2140
+ ds = db[:items]
2141
+ ds.set({:x => 3})
2142
+ db.sqls.should == ['UPDATE items SET x = 3']
2211
2143
  end
2212
2144
  end
2213
2145
 
2214
-
2215
2146
  describe "Dataset#insert_multiple" do
2216
2147
  before do
2217
- c = Class.new(Sequel::Dataset) do
2218
- attr_reader :inserts
2219
- def insert(arg)
2220
- @inserts ||= []
2221
- @inserts << arg
2222
- end
2223
- end
2224
-
2225
- @d = c.new(nil)
2148
+ @db = Sequel.mock
2149
+ @ds = @db[:items]
2226
2150
  end
2227
2151
 
2228
2152
  specify "should insert all items in the supplied array" do
2229
- @d.insert_multiple [:aa, 5, 3, {1 => 2}]
2230
- @d.inserts.should == [:aa, 5, 3, {1 => 2}]
2153
+ @ds.insert_multiple(['aa', 5, 3, {:a => 2}])
2154
+ @db.sqls.should == ["INSERT INTO items VALUES ('aa')",
2155
+ "INSERT INTO items VALUES (5)",
2156
+ "INSERT INTO items VALUES (3)",
2157
+ "INSERT INTO items (a) VALUES (2)"]
2231
2158
  end
2232
2159
 
2233
2160
  specify "should pass array items through the supplied block if given" do
2234
- a = ["inevitable", "hello", "the ticking clock"]
2235
- @d.insert_multiple(a) {|i| i.gsub('l', 'r')}
2236
- @d.inserts.should == ["inevitabre", "herro", "the ticking crock"]
2161
+ @ds.insert_multiple(["inevitable", "hello", "the ticking clock"]){|i| i.gsub('l', 'r')}
2162
+ @db.sqls.should == ["INSERT INTO items VALUES ('inevitabre')",
2163
+ "INSERT INTO items VALUES ('herro')",
2164
+ "INSERT INTO items VALUES ('the ticking crock')"]
2237
2165
  end
2238
2166
  end
2239
2167
 
2240
2168
  describe "Dataset aggregate methods" do
2241
2169
  before do
2242
- c = Class.new(Sequel::Dataset) do
2243
- def fetch_rows(sql)
2244
- yield({1 => sql})
2245
- end
2246
- end
2247
- @d = c.new(nil).from(:test)
2170
+ @d = Sequel.mock(:fetch=>proc{|s| {1=>s}})[:test]
2248
2171
  end
2249
2172
 
2250
2173
  specify "should include min" do
@@ -2278,116 +2201,88 @@ end
2278
2201
 
2279
2202
  describe "Dataset#range" do
2280
2203
  before do
2281
- c = Class.new(Sequel::Dataset) do
2282
- class_variable_set(:@@sql, nil)
2283
-
2284
- def last_sql; self.class.send(:class_variable_get, :@@sql); end
2285
-
2286
- def fetch_rows(sql)
2287
- self.class.send(:class_variable_set, :@@sql, sql)
2288
- yield(:v1 => 1, :v2 => 10)
2289
- end
2290
- end
2291
- @d = c.new(nil).from(:test)
2204
+ @db = Sequel.mock(:fetch=>{:v1 => 1, :v2 => 10})
2205
+ @ds = @db[:test]
2292
2206
  end
2293
2207
 
2294
2208
  specify "should generate a correct SQL statement" do
2295
- @d.range(:stamp)
2296
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"
2209
+ @ds.range(:stamp)
2210
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"]
2297
2211
 
2298
- @d.filter(:price.sql_number > 100).range(:stamp)
2299
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"
2212
+ @ds.filter(:price.sql_number > 100).range(:stamp)
2213
+ @db.sqls.should == ["SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"]
2300
2214
  end
2301
2215
 
2302
2216
  specify "should return a range object" do
2303
- @d.range(:tryme).should == (1..10)
2217
+ @ds.range(:tryme).should == (1..10)
2304
2218
  end
2305
2219
 
2306
2220
  specify "should use a subselect for the same conditions as count" do
2307
- @d.order(:stamp).limit(5).range(:stamp).should == (1..10)
2308
- @d.last_sql.should == 'SELECT min(stamp) AS v1, max(stamp) AS v2 FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1'
2221
+ @ds.order(:stamp).limit(5).range(:stamp).should == (1..10)
2222
+ @db.sqls.should == ['SELECT min(stamp) AS v1, max(stamp) AS v2 FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1']
2309
2223
  end
2310
2224
  end
2311
2225
 
2312
2226
  describe "Dataset#interval" do
2313
2227
  before do
2314
- c = Class.new(Sequel::Dataset) do
2315
- class_variable_set(:@@sql, nil)
2316
-
2317
- def last_sql; self.class.send(:class_variable_get, :@@sql); end
2318
-
2319
- def fetch_rows(sql)
2320
- self.class.send(:class_variable_set, :@@sql, sql)
2321
- yield(:v => 1234)
2322
- end
2323
- end
2324
- @d = c.new(nil).from(:test)
2228
+ @db = Sequel.mock(:fetch=>{:v => 1234})
2229
+ @ds = @db[:test]
2325
2230
  end
2326
2231
 
2327
- specify "should generate a correct SQL statement" do
2328
- @d.interval(:stamp)
2329
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"
2232
+ specify "should generate the correct SQL statement" do
2233
+ @ds.interval(:stamp)
2234
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"]
2330
2235
 
2331
- @d.filter(:price.sql_number > 100).interval(:stamp)
2332
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"
2333
- end
2334
-
2335
- specify "should return an integer" do
2336
- @d.interval(:tryme).should == 1234
2236
+ @ds.filter(:price.sql_number > 100).interval(:stamp)
2237
+ @db.sqls.should == ["SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"]
2337
2238
  end
2338
2239
 
2339
2240
  specify "should use a subselect for the same conditions as count" do
2340
- @d.order(:stamp).limit(5).interval(:stamp).should == 1234
2341
- @d.last_sql.should == 'SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1'
2241
+ @ds.order(:stamp).limit(5).interval(:stamp).should == 1234
2242
+ @db.sqls.should == ['SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1']
2342
2243
  end
2343
2244
  end
2344
2245
 
2345
2246
  describe "Dataset #first and #last" do
2346
2247
  before do
2347
- @c = Class.new(Sequel::Dataset) do
2348
- def each(&block)
2349
- s = select_sql
2350
- x = [:a,1,:b,2,s]
2351
- i = /LIMIT (\d+)/.match(s)[1].to_i.times{yield x}
2352
- end
2353
- end
2354
- @d = @c.new(nil).from(:test)
2248
+ @db = Sequel.mock(:fetch=>proc{|s| {:s=>s}})
2249
+ @d = @db[:test]
2355
2250
  end
2356
2251
 
2357
2252
  specify "should return a single record if no argument is given" do
2358
- @d.order(:a).first.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a LIMIT 1']
2359
- @d.order(:a).last.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a DESC LIMIT 1']
2253
+ @d.order(:a).first.should == {:s=>'SELECT * FROM test ORDER BY a LIMIT 1'}
2254
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2360
2255
  end
2361
2256
 
2362
2257
  specify "should return the first/last matching record if argument is not an Integer" do
2363
- @d.order(:a).first(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1']
2364
- @d.order(:a).first('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1']
2365
- @d.order(:a).last(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1']
2366
- @d.order(:a).last('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1']
2258
+ @d.order(:a).first(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1'}
2259
+ @d.order(:a).first('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1'}
2260
+ @d.order(:a).last(:z => 26).should == {:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1'}
2261
+ @d.order(:a).last('z = ?', 15).should == {:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1'}
2367
2262
  end
2368
2263
 
2369
2264
  specify "should set the limit and return an array of records if the given number is > 1" do
2370
2265
  i = rand(10) + 10
2371
- r = @d.order(:a).first(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a LIMIT #{i}"]] * i
2266
+ r = @d.order(:a).first(i).should == [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
2372
2267
  i = rand(10) + 10
2373
- r = @d.order(:a).last(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a DESC LIMIT #{i}"]] * i
2268
+ r = @d.order(:a).last(i).should == [{:s=>"SELECT * FROM test ORDER BY a DESC LIMIT #{i}"}]
2374
2269
  end
2375
2270
 
2376
2271
  specify "should return the first matching record if a block is given without an argument" do
2377
- @d.first{:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) LIMIT 1']
2378
- @d.order(:name).last{:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1']
2272
+ @d.first{:z.sql_number > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) LIMIT 1'}
2273
+ @d.order(:name).last{:z.sql_number > 26}.should == {:s=>'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1'}
2379
2274
  end
2380
2275
 
2381
2276
  specify "should combine block and standard argument filters if argument is not an Integer" do
2382
- @d.first(:y=>25){:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1']
2383
- @d.order(:name).last('y = ?', 16){:z.sql_number > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 16)) ORDER BY name DESC LIMIT 1']
2277
+ @d.first(:y=>25){:z.sql_number > 26}.should == {:s=>'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1'}
2278
+ @d.order(:name).last('y = ?', 16){:z.sql_number > 26}.should == {:s=>'SELECT * FROM test WHERE ((z > 26) AND (y = 16)) ORDER BY name DESC LIMIT 1'}
2384
2279
  end
2385
2280
 
2386
2281
  specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
2387
2282
  i = rand(10) + 10
2388
- r = @d.order(:a).first(i){:z.sql_number > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"]] * i
2283
+ r = @d.order(:a).first(i){:z.sql_number > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
2389
2284
  i = rand(10) + 10
2390
- r = @d.order(:a).last(i){:z.sql_number > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"]] * i
2285
+ r = @d.order(:a).last(i){:z.sql_number > 26}.should == [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"}]
2391
2286
  end
2392
2287
 
2393
2288
  specify "#last should raise if no order is given" do
@@ -2398,10 +2293,10 @@ describe "Dataset #first and #last" do
2398
2293
  end
2399
2294
 
2400
2295
  specify "#last should invert the order" do
2401
- @d.order(:a).last.pop.should == 'SELECT * FROM test ORDER BY a DESC LIMIT 1'
2402
- @d.order(:b.desc).last.pop.should == 'SELECT * FROM test ORDER BY b ASC LIMIT 1'
2403
- @d.order(:c, :d).last.pop.should == 'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'
2404
- @d.order(:e.desc, :f).last.pop.should == 'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'
2296
+ @d.order(:a).last.should == {:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1'}
2297
+ @d.order(:b.desc).last.should == {:s=>'SELECT * FROM test ORDER BY b ASC LIMIT 1'}
2298
+ @d.order(:c, :d).last.should == {:s=>'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'}
2299
+ @d.order(:e.desc, :f).last.should == {:s=>'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'}
2405
2300
  end
2406
2301
  end
2407
2302
 
@@ -2412,55 +2307,37 @@ describe "Dataset compound operations" do
2412
2307
  end
2413
2308
 
2414
2309
  specify "should support UNION and UNION ALL" do
2415
- @a.union(@b).sql.should == \
2416
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
2417
- @b.union(@a, true).sql.should == \
2418
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2419
- @b.union(@a, :all=>true).sql.should == \
2420
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2310
+ @a.union(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1"
2311
+ @b.union(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2312
+ @b.union(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2421
2313
  end
2422
2314
 
2423
2315
  specify "should support INTERSECT and INTERSECT ALL" do
2424
- @a.intersect(@b).sql.should == \
2425
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
2426
- @b.intersect(@a, true).sql.should == \
2427
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2428
- @b.intersect(@a, :all=>true).sql.should == \
2429
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2316
+ @a.intersect(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS t1"
2317
+ @b.intersect(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2318
+ @b.intersect(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2430
2319
  end
2431
2320
 
2432
2321
  specify "should support EXCEPT and EXCEPT ALL" do
2433
- @a.except(@b).sql.should == \
2434
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
2435
- @b.except(@a, true).sql.should == \
2436
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2437
- @b.except(@a, :all=>true).sql.should == \
2438
- "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2322
+ @a.except(@b).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1"
2323
+ @b.except(@a, true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2324
+ @b.except(@a, :all=>true).sql.should == "SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2439
2325
  end
2440
2326
 
2441
2327
  specify "should support :alias option for specifying identifier" do
2442
- @a.union(@b, :alias=>:xx).sql.should == \
2443
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS xx"
2444
- @a.intersect(@b, :alias=>:xx).sql.should == \
2445
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS xx"
2446
- @a.except(@b, :alias=>:xx).sql.should == \
2447
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS xx"
2328
+ @a.union(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS xx"
2329
+ @a.intersect(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)) AS xx"
2330
+ @a.except(@b, :alias=>:xx).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS xx"
2448
2331
  end
2449
2332
 
2450
2333
  specify "should support :from_self=>false option to not wrap the compound in a SELECT * FROM (...)" do
2451
- @b.union(@a, :from_self=>false).sql.should == \
2452
- "SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
2453
- @b.intersect(@a, :from_self=>false).sql.should == \
2454
- "SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
2455
- @b.except(@a, :from_self=>false).sql.should == \
2456
- "SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
2334
+ @b.union(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) UNION SELECT * FROM a WHERE (z = 1)"
2335
+ @b.intersect(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)"
2336
+ @b.except(@a, :from_self=>false).sql.should == "SELECT * FROM b WHERE (z = 2) EXCEPT SELECT * FROM a WHERE (z = 1)"
2457
2337
 
2458
- @b.union(@a, :from_self=>false, :all=>true).sql.should == \
2459
- "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
2460
- @b.intersect(@a, :from_self=>false, :all=>true).sql.should == \
2461
- "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
2462
- @b.except(@a, :from_self=>false, :all=>true).sql.should == \
2463
- "SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
2338
+ @b.union(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
2339
+ @b.intersect(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
2340
+ @b.except(@a, :from_self=>false, :all=>true).sql.should == "SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
2464
2341
  end
2465
2342
 
2466
2343
  specify "should raise an InvalidOperation if INTERSECT or EXCEPT is used and they are not supported" do
@@ -2480,141 +2357,105 @@ describe "Dataset compound operations" do
2480
2357
  end
2481
2358
 
2482
2359
  specify "should handle chained compound operations" do
2483
- @a.union(@b).union(@a, true).sql.should == \
2484
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1 UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2485
- @a.intersect(@b, true).intersect(@a).sql.should == \
2486
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2)) AS t1 INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1"
2487
- @a.except(@b).except(@a, true).sql.should == \
2488
- "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1 EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2360
+ @a.union(@b).union(@a, true).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)) AS t1 UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2361
+ @a.intersect(@b, true).intersect(@a).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM b WHERE (z = 2)) AS t1 INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1"
2362
+ @a.except(@b).except(@a, true).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)) AS t1 EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1"
2489
2363
  end
2490
2364
 
2491
2365
  specify "should use a subselect when using a compound operation with a dataset that already has a compound operation" do
2492
- @a.union(@b.union(@a, true)).sql.should == \
2493
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2494
- @a.intersect(@b.intersect(@a), true).sql.should == \
2495
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2496
- @a.except(@b.except(@a, true)).sql.should == \
2497
- "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2366
+ @a.union(@b.union(@a, true)).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM (SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2367
+ @a.intersect(@b.intersect(@a), true).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) INTERSECT ALL SELECT * FROM (SELECT * FROM b WHERE (z = 2) INTERSECT SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2368
+ @a.except(@b.except(@a, true)).sql.should == "SELECT * FROM (SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM (SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)) AS t1) AS t1"
2498
2369
  end
2499
2370
 
2500
2371
  specify "should order and limit properly when using UNION, INTERSECT, or EXCEPT" do
2501
2372
  @dataset = Sequel::Dataset.new(nil).from(:test)
2502
- @dataset.union(@dataset).limit(2).sql.should ==
2503
- "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
2504
- @dataset.limit(2).intersect(@dataset).sql.should ==
2505
- "SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
2506
- @dataset.except(@dataset.limit(2)).sql.should ==
2507
- "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
2508
-
2509
- @dataset.union(@dataset).order(:num).sql.should ==
2510
- "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
2511
- @dataset.order(:num).intersect(@dataset).sql.should ==
2512
- "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1 INTERSECT SELECT * FROM test) AS t1"
2513
- @dataset.except(@dataset.order(:num)).sql.should ==
2514
- "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1) AS t1"
2373
+ @dataset.union(@dataset).limit(2).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 LIMIT 2"
2374
+ @dataset.limit(2).intersect(@dataset).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1 INTERSECT SELECT * FROM test) AS t1"
2375
+ @dataset.except(@dataset.limit(2)).sql.should == "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test LIMIT 2) AS t1) AS t1"
2376
+
2377
+ @dataset.union(@dataset).order(:num).sql.should == "SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test) AS t1 ORDER BY num"
2378
+ @dataset.order(:num).intersect(@dataset).sql.should == "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1 INTERSECT SELECT * FROM test) AS t1"
2379
+ @dataset.except(@dataset.order(:num)).sql.should == "SELECT * FROM (SELECT * FROM test EXCEPT SELECT * FROM (SELECT * FROM test ORDER BY num) AS t1) AS t1"
2515
2380
 
2516
2381
  @dataset.limit(2).order(:a).union(@dataset.limit(3).order(:b)).order(:c).limit(4).sql.should ==
2517
2382
  "SELECT * FROM (SELECT * FROM (SELECT * FROM test ORDER BY a LIMIT 2) AS t1 UNION SELECT * FROM (SELECT * FROM test ORDER BY b LIMIT 3) AS t1) AS t1 ORDER BY c LIMIT 4"
2518
2383
  end
2519
2384
 
2385
+ specify "should hoist WITH clauses in given dataset if dataset doesn't support WITH in subselect" do
2386
+ ds = Sequel::Dataset.new(nil)
2387
+ ds.meta_def(:supports_cte?){true}
2388
+ ds.meta_def(:supports_cte_in_subselect?){false}
2389
+ ds.from(:a).union(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM a UNION SELECT * FROM c'
2390
+ ds.from(:a).except(ds.from(:c).with(:c, ds.from(:d))).sql.should == 'WITH c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a EXCEPT SELECT * FROM c) AS t1'
2391
+ ds.from(:a).with(:a, ds.from(:b)).intersect(ds.from(:c).with(:c, ds.from(:d)), :from_self=>false).sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM a INTERSECT SELECT * FROM c'
2392
+ end
2520
2393
  end
2521
2394
 
2522
2395
  describe "Dataset#[]" do
2523
2396
  before do
2524
- @c = Class.new(Sequel::Dataset) do
2525
- class_variable_set(:@@last_dataset, nil)
2526
-
2527
- def self.last_dataset
2528
- class_variable_get(:@@last_dataset)
2529
- end
2530
-
2531
- def single_record
2532
- self.class.send(:class_variable_set, :@@last_dataset, opts ? clone(opts) : self)
2533
- {1 => 2, 3 => 4}
2534
- end
2535
- end
2536
- @d = @c.new(nil).from(:test)
2397
+ @db = Sequel.mock(:fetch=>{1 => 2, 3 => 4})
2398
+ @d = @db[:items]
2537
2399
  end
2538
2400
 
2539
2401
  specify "should return a single record filtered according to the given conditions" do
2540
2402
  @d[:name => 'didi'].should == {1 => 2, 3 => 4}
2541
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "(name = 'didi')"
2403
+ @db.sqls.should == ["SELECT * FROM items WHERE (name = 'didi') LIMIT 1"]
2542
2404
 
2543
2405
  @d[:id => 5..45].should == {1 => 2, 3 => 4}
2544
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "((id >= 5) AND (id <= 45))"
2406
+ @db.sqls.should == ["SELECT * FROM items WHERE ((id >= 5) AND (id <= 45)) LIMIT 1"]
2545
2407
  end
2546
2408
  end
2547
2409
 
2548
2410
  describe "Dataset#single_record" do
2549
2411
  before do
2550
- @c = Class.new(Sequel::Dataset) do
2551
- def fetch_rows(sql)
2552
- yield sql
2553
- end
2554
- end
2555
- @cc = Class.new(@c) do
2556
- def fetch_rows(sql); end
2557
- end
2558
-
2559
- @d = @c.new(nil).from(:test)
2560
- @e = @cc.new(nil).from(:test)
2412
+ @db = Sequel.mock
2561
2413
  end
2562
2414
 
2563
2415
  specify "should call each with a limit of 1 and return the record" do
2564
- @d.single_record.should == 'SELECT * FROM test LIMIT 1'
2416
+ @db.fetch = {:a=>1}
2417
+ @db[:test].single_record.should == {:a=>1}
2418
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2565
2419
  end
2566
2420
 
2567
2421
  specify "should return nil if no record is present" do
2568
- @e.single_record.should be_nil
2422
+ @db[:test].single_record.should be_nil
2423
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2569
2424
  end
2570
2425
  end
2571
2426
 
2572
2427
  describe "Dataset#single_value" do
2573
2428
  before do
2574
- @c = Class.new(Sequel::Dataset) do
2575
- def fetch_rows(sql)
2576
- yield({1 => sql})
2577
- end
2578
- end
2579
- @cc = Class.new(@c) do
2580
- def fetch_rows(sql); end
2581
- end
2582
-
2583
- @d = @c.new(nil).from(:test)
2584
- @e = @cc.new(nil).from(:test)
2429
+ @db = Sequel.mock
2585
2430
  end
2586
2431
 
2587
2432
  specify "should call each and return the first value of the first record" do
2588
- @d.single_value.should == 'SELECT * FROM test LIMIT 1'
2433
+ @db.fetch = {:a=>1}
2434
+ @db[:test].single_value.should == 1
2435
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2589
2436
  end
2590
2437
 
2591
2438
  specify "should return nil if no records" do
2592
- @e.single_value.should be_nil
2439
+ @db[:test].single_value.should be_nil
2440
+ @db.sqls.should == ['SELECT * FROM test LIMIT 1']
2593
2441
  end
2594
2442
 
2595
2443
  it "should work on a graphed_dataset" do
2596
- @d.should_receive(:columns).twice.and_return([:a])
2597
- @d.graph(@d, [:a], :table_alias=>:test2).single_value.should == 'SELECT test.a, test2.a AS test2_a FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1'
2444
+ @db.fetch = {:a=>1}
2445
+ ds = @db[:test].columns(:a)
2446
+ ds.graph(ds, [:a], :table_alias=>:test2).single_value.should == 1
2447
+ @db.sqls.should == ['SELECT test.a, test2.a AS test2_a FROM test LEFT OUTER JOIN test AS test2 USING (a) LIMIT 1']
2598
2448
  end
2599
2449
  end
2600
2450
 
2601
2451
  describe "Dataset#get" do
2602
2452
  before do
2603
- @c = Class.new(Sequel::Dataset) do
2604
- attr_reader :last_sql
2605
-
2606
- def fetch_rows(sql)
2607
- @last_sql = sql
2608
- yield(:name => sql)
2609
- end
2610
- end
2611
-
2612
- @d = @c.new(nil).from(:test)
2453
+ @d = Sequel.mock(:fetch=>proc{|s| {:name=>s}})[:test]
2613
2454
  end
2614
2455
 
2615
2456
  specify "should select the specified column and fetch its value" do
2616
2457
  @d.get(:name).should == "SELECT name FROM test LIMIT 1"
2617
- @d.get(:abc).should == "SELECT abc FROM test LIMIT 1" # the first available value is returned always
2458
+ @d.get(:abc).should == "SELECT abc FROM test LIMIT 1"
2618
2459
  end
2619
2460
 
2620
2461
  specify "should work with filters" do
@@ -2637,239 +2478,206 @@ end
2637
2478
 
2638
2479
  describe "Dataset#set_row_proc" do
2639
2480
  before do
2640
- @c = Class.new(Sequel::Dataset) do
2641
- def fetch_rows(sql, &block)
2642
- # yield a hash with kind as the 1 bit of a number
2643
- (1..10).each {|i| block.call({:kind => i[0]})}
2644
- end
2645
- end
2646
- @dataset = @c.new(nil).from(:items)
2481
+ @db = Sequel.mock(:fetch=>[{:a=>1}, {:a=>2}])
2482
+ @dataset = @db[:items]
2483
+ @dataset.row_proc = proc{|h| h[:der] = h[:a] + 2; h}
2647
2484
  end
2648
2485
 
2649
2486
  specify "should cause dataset to pass all rows through the filter" do
2650
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
2651
-
2652
2487
  rows = @dataset.all
2653
- rows.size.should == 10
2654
-
2655
- rows.each {|r| r[:der].should == (r[:kind] + 2)}
2488
+ rows.map{|h| h[:der]}.should == [3, 4]
2489
+ @db.sqls.should == ['SELECT * FROM items']
2656
2490
  end
2657
2491
 
2658
2492
  specify "should be copied over when dataset is cloned" do
2659
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
2660
-
2661
- @dataset.filter(:a => 1).first.should == {:kind => 1, :der => 3}
2493
+ @dataset.filter(:a => 1).all.should == [{:a=>1, :der=>3}, {:a=>2, :der=>4}]
2662
2494
  end
2663
2495
  end
2664
2496
 
2665
2497
  describe "Dataset#<<" do
2666
2498
  before do
2667
- @d = Sequel::Dataset.new(nil)
2668
- @d.meta_def(:insert) do |*args|
2669
- 1234567890
2670
- end
2499
+ @db = Sequel.mock
2671
2500
  end
2672
-
2501
+
2673
2502
  specify "should call #insert" do
2674
- (@d << {:name => 1}).should == 1234567890
2503
+ @db[:items] << {:name => 1}
2504
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)']
2505
+ end
2506
+
2507
+ specify "should be chainable" do
2508
+ @db[:items] << {:name => 1} << @db[:old_items].select(:name)
2509
+ @db.sqls.should == ['INSERT INTO items (name) VALUES (1)', 'INSERT INTO items SELECT name FROM old_items']
2675
2510
  end
2676
2511
  end
2677
2512
 
2678
2513
  describe "Dataset#columns" do
2679
2514
  before do
2680
- @dataset = DummyDataset.new(nil).from(:items)
2681
- @dataset.meta_def(:columns=) {|c| @columns = c}
2682
- i = 'a'
2683
- @dataset.meta_def(:each){@columns = select_sql + i; i = i.next}
2515
+ @dataset = Sequel.mock[:items]
2684
2516
  end
2685
2517
 
2686
2518
  specify "should return the value of @columns if @columns is not nil" do
2687
- @dataset.columns = [:a, :b, :c]
2688
- @dataset.columns.should == [:a, :b, :c]
2519
+ @dataset.columns(:a, :b, :c).columns.should == [:a, :b, :c]
2520
+ @dataset.db.sqls.should == []
2689
2521
  end
2690
2522
 
2691
2523
  specify "should attempt to get a single record and return @columns if @columns is nil" do
2692
- @dataset.columns = nil
2693
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2694
- @dataset.opts[:from] = [:nana]
2695
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2524
+ @dataset.db.columns = [:a]
2525
+ @dataset.columns.should == [:a]
2526
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2527
+ end
2528
+
2529
+ specify "should be cleared if you change the selected columns" do
2530
+ @dataset.db.columns = [[:a], [:b]]
2531
+ @dataset.columns.should == [:a]
2532
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2533
+ @dataset.columns.should == [:a]
2534
+ @dataset.db.sqls.should == []
2535
+ ds = @dataset.select{foo{}}
2536
+ ds.columns.should == [:b]
2537
+ @dataset.db.sqls.should == ['SELECT foo() FROM items LIMIT 1']
2538
+ end
2539
+
2540
+ specify "should be cleared if you change the FROM table" do
2541
+ @dataset.db.columns = [[:a], [:b]]
2542
+ @dataset.columns.should == [:a]
2543
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2544
+ ds = @dataset.from(:foo)
2545
+ ds.columns.should == [:b]
2546
+ @dataset.db.sqls.should == ['SELECT * FROM foo LIMIT 1']
2547
+ end
2548
+
2549
+ specify "should be cleared if you join a table to the dataset" do
2550
+ @dataset.db.columns = [[:a], [:a, :b]]
2551
+ @dataset.columns.should == [:a]
2552
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2553
+ ds = @dataset.cross_join(:foo)
2554
+ ds.columns.should == [:a, :b]
2555
+ @dataset.db.sqls.should == ['SELECT * FROM items CROSS JOIN foo LIMIT 1']
2556
+ end
2557
+
2558
+ specify "should be cleared if you set custom SQL for the dataset" do
2559
+ @dataset.db.columns = [[:a], [:b]]
2560
+ @dataset.columns.should == [:a]
2561
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2562
+ ds = @dataset.with_sql('SELECT b FROM foo')
2563
+ ds.columns.should == [:b]
2564
+ @dataset.db.sqls.should == ['SELECT b FROM foo']
2696
2565
  end
2697
2566
 
2698
2567
  specify "should ignore any filters, orders, or DISTINCT clauses" do
2568
+ @dataset.db.columns = [:a]
2699
2569
  @dataset.filter!(:b=>100).order!(:b).distinct!
2700
- @dataset.columns = nil
2701
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2570
+ @dataset.columns.should == [:a]
2571
+ @dataset.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2702
2572
  end
2703
2573
  end
2704
2574
 
2705
2575
  describe "Dataset#columns!" do
2706
- before do
2707
- @dataset = DummyDataset.new(nil).from(:items)
2708
- i = 'a'
2709
- @dataset.meta_def(:each){@columns = select_sql + i; i = i.next}
2710
- end
2711
-
2712
2576
  specify "should always attempt to get a record and return @columns" do
2713
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1a'
2714
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1b'
2715
- @dataset.opts[:from] = [:nana]
2716
- @dataset.columns!.should == 'SELECT * FROM nana LIMIT 1c'
2577
+ ds = Sequel.mock(:columns=>[[:a, :b, :c], [:d, :e, :f]])[:items]
2578
+ ds.columns!.should == [:a, :b, :c]
2579
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2580
+ ds.columns!.should == [:d, :e, :f]
2581
+ ds.db.sqls.should == ['SELECT * FROM items LIMIT 1']
2717
2582
  end
2718
2583
  end
2719
2584
 
2720
2585
  describe "Dataset#import" do
2721
2586
  before do
2722
- @dbc = Class.new(Sequel::Database) do
2723
- attr_reader :sqls
2724
-
2725
- def execute(sql, opts={})
2726
- @sqls ||= []
2727
- @sqls << sql
2728
- end
2729
- alias execute_dui execute
2730
-
2731
- def transaction(opts={})
2732
- @sqls ||= []
2733
- @sqls << 'BEGIN'
2734
- yield
2735
- @sqls << 'COMMIT'
2736
- end
2737
- end
2738
- @db = @dbc.new
2739
-
2740
- @ds = Sequel::Dataset.new(@db).from(:items)
2741
-
2742
- @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2587
+ @db = Sequel.mock
2588
+ @ds = @db[:items]
2743
2589
  end
2744
2590
 
2745
2591
  specify "should accept string keys as column names" do
2746
2592
  @ds.import(['x', 'y'], [[1, 2], [3, 4]])
2747
- @db.sqls.should == [
2748
- 'BEGIN',
2593
+ @db.sqls.should == ['BEGIN',
2749
2594
  "INSERT INTO items (x, y) VALUES (1, 2)",
2750
2595
  "INSERT INTO items (x, y) VALUES (3, 4)",
2751
- 'COMMIT'
2752
- ]
2596
+ 'COMMIT']
2753
2597
  end
2754
2598
 
2755
2599
  specify "should accept a columns array and a values array" do
2756
2600
  @ds.import([:x, :y], [[1, 2], [3, 4]])
2757
- @db.sqls.should == [
2758
- 'BEGIN',
2601
+ @db.sqls.should == ['BEGIN',
2759
2602
  "INSERT INTO items (x, y) VALUES (1, 2)",
2760
2603
  "INSERT INTO items (x, y) VALUES (3, 4)",
2761
- 'COMMIT'
2762
- ]
2604
+ 'COMMIT']
2763
2605
  end
2764
2606
 
2765
2607
  specify "should accept a columns array and a dataset" do
2766
- @ds2 = Sequel::Dataset.new(@db).from(:cats).filter(:purr => true).select(:a, :b)
2608
+ @ds2 = @ds.from(:cats).filter(:purr => true).select(:a, :b)
2767
2609
 
2768
2610
  @ds.import([:x, :y], @ds2)
2769
- @db.sqls.should == [
2770
- 'BEGIN',
2611
+ @db.sqls.should == ['BEGIN',
2771
2612
  "INSERT INTO items (x, y) SELECT a, b FROM cats WHERE (purr IS TRUE)",
2772
- 'COMMIT'
2773
- ]
2613
+ 'COMMIT']
2774
2614
  end
2775
2615
 
2776
2616
  specify "should accept a columns array and a values array with :commit_every option" do
2777
2617
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :commit_every => 3)
2778
- @db.sqls.should == [
2779
- 'BEGIN',
2618
+ @db.sqls.should == ['BEGIN',
2780
2619
  "INSERT INTO items (x, y) VALUES (1, 2)",
2781
2620
  "INSERT INTO items (x, y) VALUES (3, 4)",
2782
2621
  "INSERT INTO items (x, y) VALUES (5, 6)",
2783
- 'COMMIT',
2784
- ]
2622
+ 'COMMIT']
2785
2623
  end
2786
- specify "should accept a columns array and a values array with slice option" do
2624
+ specify "should accept a columns array and a values array with :slice option" do
2787
2625
  @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2788
- @db.sqls.should == [
2789
- 'BEGIN',
2626
+ @db.sqls.should == ['BEGIN',
2790
2627
  "INSERT INTO items (x, y) VALUES (1, 2)",
2791
2628
  "INSERT INTO items (x, y) VALUES (3, 4)",
2792
2629
  'COMMIT',
2793
2630
  'BEGIN',
2794
2631
  "INSERT INTO items (x, y) VALUES (5, 6)",
2795
- 'COMMIT'
2796
- ]
2632
+ 'COMMIT']
2797
2633
  end
2798
2634
  end
2799
2635
 
2800
2636
  describe "Dataset#multi_insert" do
2801
2637
  before do
2802
- @dbc = Class.new do
2803
- attr_reader :sqls
2804
-
2805
- def execute(sql, opts={})
2806
- @sqls ||= []
2807
- @sqls << sql
2808
- end
2809
- alias execute_dui execute
2810
-
2811
- def transaction(opts={})
2812
- @sqls ||= []
2813
- @sqls << 'BEGIN'
2814
- yield
2815
- @sqls << 'COMMIT'
2816
- end
2817
- end
2818
- @db = @dbc.new
2819
-
2820
- @ds = Sequel::Dataset.new(@db).from(:items)
2821
-
2638
+ @db = Sequel.mock
2639
+ @ds = @db[:items]
2822
2640
  @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2823
2641
  end
2824
2642
 
2825
2643
  specify "should issue multiple insert statements inside a transaction" do
2826
2644
  @ds.multi_insert(@list)
2827
- @db.sqls.should == [
2828
- 'BEGIN',
2645
+ @db.sqls.should == ['BEGIN',
2829
2646
  "INSERT INTO items (name) VALUES ('abc')",
2830
2647
  "INSERT INTO items (name) VALUES ('def')",
2831
2648
  "INSERT INTO items (name) VALUES ('ghi')",
2832
- 'COMMIT'
2833
- ]
2649
+ 'COMMIT']
2834
2650
  end
2835
2651
 
2836
2652
  specify "should handle different formats for tables" do
2837
2653
  @ds = @ds.from(:sch__tab)
2838
2654
  @ds.multi_insert(@list)
2839
- @db.sqls.should == [
2840
- 'BEGIN',
2655
+ @db.sqls.should == ['BEGIN',
2841
2656
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2842
2657
  "INSERT INTO sch.tab (name) VALUES ('def')",
2843
2658
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2844
- 'COMMIT'
2845
- ]
2846
- @db.sqls.clear
2659
+ 'COMMIT']
2847
2660
 
2848
2661
  @ds = @ds.from(:tab.qualify(:sch))
2849
2662
  @ds.multi_insert(@list)
2850
- @db.sqls.should == [
2851
- 'BEGIN',
2663
+ @db.sqls.should == ['BEGIN',
2852
2664
  "INSERT INTO sch.tab (name) VALUES ('abc')",
2853
2665
  "INSERT INTO sch.tab (name) VALUES ('def')",
2854
2666
  "INSERT INTO sch.tab (name) VALUES ('ghi')",
2855
- 'COMMIT'
2856
- ]
2857
- @db.sqls.clear
2667
+ 'COMMIT']
2668
+
2858
2669
  @ds = @ds.from(:sch__tab.identifier)
2859
2670
  @ds.multi_insert(@list)
2860
- @db.sqls.should == [
2861
- 'BEGIN',
2671
+ @db.sqls.should == ['BEGIN',
2862
2672
  "INSERT INTO sch__tab (name) VALUES ('abc')",
2863
2673
  "INSERT INTO sch__tab (name) VALUES ('def')",
2864
2674
  "INSERT INTO sch__tab (name) VALUES ('ghi')",
2865
- 'COMMIT'
2866
- ]
2675
+ 'COMMIT']
2867
2676
  end
2868
2677
 
2869
2678
  specify "should accept the :commit_every option for committing every x records" do
2870
2679
  @ds.multi_insert(@list, :commit_every => 1)
2871
- @db.sqls.should == [
2872
- 'BEGIN',
2680
+ @db.sqls.should == ['BEGIN',
2873
2681
  "INSERT INTO items (name) VALUES ('abc')",
2874
2682
  'COMMIT',
2875
2683
  'BEGIN',
@@ -2877,36 +2685,31 @@ describe "Dataset#multi_insert" do
2877
2685
  'COMMIT',
2878
2686
  'BEGIN',
2879
2687
  "INSERT INTO items (name) VALUES ('ghi')",
2880
- 'COMMIT'
2881
- ]
2688
+ 'COMMIT']
2882
2689
  end
2883
2690
 
2884
2691
  specify "should accept the :slice option for committing every x records" do
2885
2692
  @ds.multi_insert(@list, :slice => 2)
2886
- @db.sqls.should == [
2887
- 'BEGIN',
2693
+ @db.sqls.should == ['BEGIN',
2888
2694
  "INSERT INTO items (name) VALUES ('abc')",
2889
2695
  "INSERT INTO items (name) VALUES ('def')",
2890
2696
  'COMMIT',
2891
2697
  'BEGIN',
2892
2698
  "INSERT INTO items (name) VALUES ('ghi')",
2893
- 'COMMIT'
2894
- ]
2699
+ 'COMMIT']
2895
2700
  end
2896
2701
 
2897
2702
  specify "should accept string keys as column names" do
2898
2703
  @ds.multi_insert([{'x'=>1, 'y'=>2}, {'x'=>3, 'y'=>4}])
2899
- @db.sqls.should == [
2900
- 'BEGIN',
2704
+ @db.sqls.should == ['BEGIN',
2901
2705
  "INSERT INTO items (x, y) VALUES (1, 2)",
2902
2706
  "INSERT INTO items (x, y) VALUES (3, 4)",
2903
- 'COMMIT'
2904
- ]
2707
+ 'COMMIT']
2905
2708
  end
2906
2709
 
2907
2710
  specify "should not do anything if no hashes are provided" do
2908
2711
  @ds.multi_insert([])
2909
- @db.sqls.should be_nil
2712
+ @db.sqls.should == []
2910
2713
  end
2911
2714
  end
2912
2715
 
@@ -2951,41 +2754,21 @@ describe "Dataset" do
2951
2754
  end
2952
2755
 
2953
2756
  specify "should support chaining of bang methods" do
2954
- @d.order!(:y)
2955
- @d.filter!(:y => 1)
2956
- @d.sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2757
+ @d.order!(:y).filter!(:y => 1).sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2957
2758
  end
2958
2759
  end
2959
2760
 
2960
2761
  describe "Dataset#to_csv" do
2961
2762
  before do
2962
- @c = Class.new(Sequel::Dataset) do
2963
- attr_accessor :data
2964
- attr_accessor :columns
2965
-
2966
- def fetch_rows(sql, &block)
2967
- @data.each(&block)
2968
- end
2969
-
2970
- # naked should return self here because to_csv wants a naked result set.
2971
- def naked
2972
- self
2973
- end
2974
- end
2975
-
2976
- @ds = @c.new(nil).from(:items)
2977
- @ds.columns = [:a, :b, :c]
2978
- @ds.data = [ {:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>9} ]
2763
+ @ds = Sequel.mock(:fetch=>[{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>9}])[:items].columns(:a, :b, :c)
2979
2764
  end
2980
2765
 
2981
2766
  specify "should format a CSV representation of the records" do
2982
- @ds.to_csv.should ==
2983
- "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2767
+ @ds.to_csv.should == "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2984
2768
  end
2985
2769
 
2986
2770
  specify "should exclude column titles if so specified" do
2987
- @ds.to_csv(false).should ==
2988
- "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2771
+ @ds.to_csv(false).should == "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2989
2772
  end
2990
2773
  end
2991
2774
 
@@ -3083,66 +2866,40 @@ describe "Dataset#insert_sql" do
3083
2866
  end
3084
2867
  end
3085
2868
 
3086
- class DummyMummyDataset < Sequel::Dataset
3087
- def first
3088
- raise if @opts[:from] == [:a]
3089
- true
2869
+ describe "Dataset#inspect" do
2870
+ before do
2871
+ class ::InspectDataset < Sequel::Dataset; end
3090
2872
  end
3091
- end
3092
-
3093
- class DummyMummyDatabase < Sequel::Database
3094
- attr_reader :sqls
3095
-
3096
- def execute(sql)
3097
- @sqls ||= []
3098
- @sqls << sql
2873
+ after do
2874
+ Object.send(:remove_const, :InspectDataset) if defined?(::InspectDataset)
3099
2875
  end
3100
-
3101
- def transaction; yield; end
3102
2876
 
3103
- def dataset
3104
- DummyMummyDataset.new(self)
2877
+ specify "should include the class name and the corresponding SQL statement" do
2878
+ Sequel::Dataset.new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
2879
+ InspectDataset.new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3105
2880
  end
3106
- end
3107
2881
 
3108
- describe "Dataset#inspect" do
3109
- before do
3110
- @ds = Sequel::Dataset.new(nil).from(:blah)
3111
- end
3112
-
3113
- specify "should include the class name and the corresponding SQL statement" do
3114
- @ds.inspect.should == '#<%s: %s>' % [@ds.class.to_s, @ds.sql.inspect]
2882
+ specify "should skip anonymous classes" do
2883
+ Class.new(Class.new(Sequel::Dataset)).new(nil).from(:blah).inspect.should == '#<Sequel::Dataset: "SELECT * FROM blah">'
2884
+ Class.new(InspectDataset).new(nil).from(:blah).inspect.should == '#<InspectDataset: "SELECT * FROM blah">'
3115
2885
  end
3116
2886
  end
3117
2887
 
3118
2888
  describe "Dataset#all" do
3119
2889
  before do
3120
- @c = Class.new(Sequel::Dataset) do
3121
- def fetch_rows(sql, &block)
3122
- block.call({:x => 1, :y => 2})
3123
- block.call({:x => 3, :y => 4})
3124
- block.call(sql)
3125
- end
3126
- end
3127
- @dataset = @c.new(nil).from(:items)
2890
+ @dataset = Sequel.mock(:fetch=>[{:x => 1, :y => 2}, {:x => 3, :y => 4}])[:items]
3128
2891
  end
3129
2892
 
3130
2893
  specify "should return an array with all records" do
3131
- @dataset.all.should == [
3132
- {:x => 1, :y => 2},
3133
- {:x => 3, :y => 4},
3134
- "SELECT * FROM items"
3135
- ]
2894
+ @dataset.all.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
2895
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3136
2896
  end
3137
2897
 
3138
2898
  specify "should iterate over the array if a block is given" do
3139
2899
  a = []
3140
-
3141
- @dataset.all do |r|
3142
- a << (r.is_a?(Hash) ? r[:x] : r)
3143
- end
3144
-
3145
- a.should == [1, 3, "SELECT * FROM items"]
2900
+ @dataset.all{|r| a << r.values_at(:x, :y)}.should == [{:x => 1, :y => 2}, {:x => 3, :y => 4}]
2901
+ a.should == [[1, 2], [3, 4]]
2902
+ @dataset.db.sqls.should == ["SELECT * FROM items"]
3146
2903
  end
3147
2904
  end
3148
2905
 
@@ -3152,71 +2909,56 @@ describe "Dataset#grep" do
3152
2909
  end
3153
2910
 
3154
2911
  specify "should format a SQL filter correctly" do
3155
- @ds.grep(:title, 'ruby').sql.should ==
3156
- "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
2912
+ @ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
3157
2913
  end
3158
2914
 
3159
2915
  specify "should support multiple columns" do
3160
- @ds.grep([:title, :body], 'ruby').sql.should ==
3161
- "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
2916
+ @ds.grep([:title, :body], 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
3162
2917
  end
3163
2918
 
3164
2919
  specify "should support multiple search terms" do
3165
- @ds.grep(:title, ['abc', 'def']).sql.should ==
3166
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
2920
+ @ds.grep(:title, ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
3167
2921
  end
3168
2922
 
3169
2923
  specify "should support multiple columns and search terms" do
3170
- @ds.grep([:title, :body], ['abc', 'def']).sql.should ==
3171
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
2924
+ @ds.grep([:title, :body], ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
3172
2925
  end
3173
2926
 
3174
2927
  specify "should support the :all_patterns option" do
3175
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should ==
3176
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
2928
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
3177
2929
  end
3178
2930
 
3179
2931
  specify "should support the :all_columns option" do
3180
- @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should ==
3181
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
2932
+ @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
3182
2933
  end
3183
2934
 
3184
2935
  specify "should support the :case_insensitive option" do
3185
- @ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should ==
3186
- "SELECT * FROM posts WHERE ((title ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
2936
+ @ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
3187
2937
  end
3188
2938
 
3189
2939
  specify "should support the :all_patterns and :all_columns options together" do
3190
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should ==
3191
- "SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
2940
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
3192
2941
  end
3193
2942
 
3194
2943
  specify "should support the :all_patterns and :case_insensitive options together" do
3195
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should ==
3196
- "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
2944
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
3197
2945
  end
3198
2946
 
3199
2947
  specify "should support the :all_columns and :case_insensitive options together" do
3200
- @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should ==
3201
- "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
2948
+ @ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
3202
2949
  end
3203
2950
 
3204
2951
  specify "should support the :all_patterns, :all_columns, and :case_insensitive options together" do
3205
- @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should ==
3206
- "SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
2952
+ @ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
3207
2953
  end
3208
2954
 
3209
2955
  specify "should support regexps though the database may not support it" do
3210
- @ds.grep(:title, /ruby/).sql.should ==
3211
- "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
3212
-
3213
- @ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
3214
- "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
2956
+ @ds.grep(:title, /ruby/).sql.should == "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
2957
+ @ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
3215
2958
  end
3216
2959
 
3217
2960
  specify "should support searching against other columns" do
3218
- @ds.grep(:title, :body).sql.should ==
3219
- "SELECT * FROM posts WHERE ((title LIKE body))"
2961
+ @ds.grep(:title, :body).sql.should == "SELECT * FROM posts WHERE ((title LIKE body))"
3220
2962
  end
3221
2963
  end
3222
2964
 
@@ -3270,19 +3012,9 @@ end
3270
3012
 
3271
3013
  describe "Dataset prepared statements and bound variables " do
3272
3014
  before do
3273
- @db = Sequel::Database.new
3274
- @db.meta_def(:sqls){@sqls||=[]}
3275
- def @db.execute(sql, opts={})
3276
- sqls << sql
3277
- end
3278
- def @db.dataset
3279
- ds = super()
3280
- def ds.fetch_rows(sql, &block)
3281
- execute(sql)
3282
- end
3283
- ds
3284
- end
3015
+ @db = Sequel.mock
3285
3016
  @ds = @db[:items]
3017
+ @ds.meta_def(:insert_sql){|*v| "#{super(*v)}#{' RETURNING *' if opts.has_key?(:returning)}" }
3286
3018
  end
3287
3019
 
3288
3020
  specify "#call should take a type and bind hash and interpolate it" do
@@ -3291,11 +3023,13 @@ describe "Dataset prepared statements and bound variables " do
3291
3023
  @ds.filter(:num=>:$n).call(:delete, :n=>1)
3292
3024
  @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3293
3025
  @ds.call(:insert, {:n=>1}, :num=>:$n)
3026
+ @ds.call(:insert_select, {:n=>1}, :num=>:$n)
3294
3027
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3295
3028
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3296
3029
  'DELETE FROM items WHERE (num = 1)',
3297
3030
  'UPDATE items SET num = 2 WHERE (num = 1)',
3298
- 'INSERT INTO items (num) VALUES (1)']
3031
+ 'INSERT INTO items (num) VALUES (1)',
3032
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3299
3033
  end
3300
3034
 
3301
3035
  specify "#prepare should take a type and name and store it in the database for later use with call" do
@@ -3305,23 +3039,26 @@ describe "Dataset prepared statements and bound variables " do
3305
3039
  pss << @ds.filter(:num=>:$n).prepare(:delete, :dn)
3306
3040
  pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
3307
3041
  pss << @ds.prepare(:insert, :in, :num=>:$n)
3308
- @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :sn, :un]
3309
- [:sn, :fn, :dn, :un, :in].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3042
+ pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
3043
+ @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :ins, :sn, :un]
3044
+ [:sn, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3310
3045
  @db.call(:sn, :n=>1)
3311
3046
  @db.call(:fn, :n=>1)
3312
3047
  @db.call(:dn, :n=>1)
3313
3048
  @db.call(:un, :n=>1, :n2=>2)
3314
3049
  @db.call(:in, :n=>1)
3050
+ @db.call(:ins, :n=>1)
3315
3051
  @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3316
3052
  'SELECT * FROM items WHERE (num = 1) LIMIT 1',
3317
3053
  'DELETE FROM items WHERE (num = 1)',
3318
3054
  'UPDATE items SET num = 2 WHERE (num = 1)',
3319
- 'INSERT INTO items (num) VALUES (1)']
3055
+ 'INSERT INTO items (num) VALUES (1)',
3056
+ 'INSERT INTO items (num) VALUES (1) RETURNING *']
3320
3057
  end
3321
3058
 
3322
3059
  specify "#inspect should indicate it is a prepared statement with the prepared SQL" do
3323
3060
  @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.should == \
3324
- '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3061
+ '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
3325
3062
  end
3326
3063
 
3327
3064
  specify "should handle literal strings" do
@@ -3357,23 +3094,16 @@ end
3357
3094
 
3358
3095
  describe Sequel::Dataset::UnnumberedArgumentMapper do
3359
3096
  before do
3360
- @db = Sequel::Database.new
3361
- @db.meta_def(:sqls){@sqls||=[]}
3362
- def @db.execute(sql, opts={})
3363
- sqls << [sql, *opts[:arguments]]
3364
- end
3097
+ @db = Sequel.mock
3365
3098
  @ds = @db[:items].filter(:num=>:$n)
3366
- def @ds.fetch_rows(sql, &block)
3367
- execute(sql)
3368
- end
3369
3099
  def @ds.execute(sql, opts={}, &block)
3370
- @db.execute(sql, {:arguments=>bind_arguments}.merge(opts))
3100
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3371
3101
  end
3372
- def @ds.execute_dui(*args, &block)
3373
- execute(*args, &block)
3102
+ def @ds.execute_dui(sql, opts={}, &block)
3103
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3374
3104
  end
3375
- def @ds.execute_insert(*args, &block)
3376
- execute(*args, &block)
3105
+ def @ds.execute_insert(sql, opts={}, &block)
3106
+ super(sql, opts.merge({:arguments=>bind_arguments}), &block)
3377
3107
  end
3378
3108
  @ps = []
3379
3109
  @ps << @ds.prepare(:select, :s)
@@ -3386,64 +3116,43 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
3386
3116
  end
3387
3117
 
3388
3118
  specify "#inspect should show the actual SQL submitted to the database" do
3389
- @ps.first.inspect.should == '<Sequel::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3119
+ @ps.first.inspect.should == '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3390
3120
  end
3391
3121
 
3392
3122
  specify "should submitted the SQL to the database with placeholders and bind variables" do
3393
3123
  @ps.each{|p| p.prepared_sql; p.call(:n=>1)}
3394
- @db.sqls.should == [["SELECT * FROM items WHERE (num = ?)", 1],
3395
- ["SELECT * FROM items WHERE (num = ?)", 1],
3396
- ["SELECT * FROM items WHERE (num = ?) LIMIT 1", 1],
3397
- ["DELETE FROM items WHERE (num = ?)", 1],
3398
- ["INSERT INTO items (num) VALUES (?)", 1],
3399
- ["UPDATE items SET num = ? WHERE (num = ?)", 1, 1],
3400
- ]
3124
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]",
3125
+ "SELECT * FROM items WHERE (num = ?) -- args: [1]",
3126
+ "SELECT * FROM items WHERE (num = ?) LIMIT 1 -- args: [1]",
3127
+ "DELETE FROM items WHERE (num = ?) -- args: [1]",
3128
+ "INSERT INTO items (num) VALUES (?) -- args: [1]",
3129
+ "UPDATE items SET num = ? WHERE (num = ?) -- args: [1, 1]"]
3401
3130
  end
3402
3131
  end
3403
3132
 
3404
3133
  describe "Sequel::Dataset#server" do
3405
3134
  specify "should set the server to use for the dataset" do
3406
- @db = Sequel::Database.new
3135
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}, :d=>{}, :u=>{}})
3407
3136
  @ds = @db[:items].server(:s)
3408
- sqls = []
3409
- @db.meta_def(:execute) do |sql, opts|
3410
- sqls << [sql, opts[:server]]
3411
- end
3412
- def @ds.fetch_rows(sql, &block)
3413
- execute(sql)
3414
- end
3415
3137
  @ds.all
3416
3138
  @ds.server(:i).insert(:a=>1)
3417
3139
  @ds.server(:d).delete
3418
3140
  @ds.server(:u).update(:a=>:a+1)
3419
- sqls.should == [['SELECT * FROM items', :s],
3420
- ['INSERT INTO items (a) VALUES (1)', :i],
3421
- ['DELETE FROM items', :d],
3422
- ['UPDATE items SET a = (a + 1)', :u]]
3141
+ @db.sqls.should == ['SELECT * FROM items -- s', 'INSERT INTO items (a) VALUES (1) -- i', 'DELETE FROM items -- d', 'UPDATE items SET a = (a + 1) -- u']
3423
3142
  end
3424
3143
  end
3425
3144
 
3426
3145
  describe "Sequel::Dataset#each_server" do
3427
- before do
3428
- @db = Sequel::Database.new(:servers=>{:s=>{}, :i=>{}})
3429
- @ds = @db[:items]
3430
- sqls = @sqls = []
3431
- @db.meta_def(:execute) do |sql, opts|
3432
- sqls << [sql, opts[:server]]
3433
- end
3434
- def @ds.fetch_rows(sql, &block)
3435
- execute(sql)
3436
- end
3437
- end
3438
-
3439
3146
  specify "should yield a dataset for each server" do
3147
+ @db = Sequel.mock(:servers=>{:s=>{}, :i=>{}})
3148
+ @ds = @db[:items]
3440
3149
  @ds.each_server do |ds|
3441
3150
  ds.should be_a_kind_of(Sequel::Dataset)
3442
3151
  ds.should_not == @ds
3443
3152
  ds.sql.should == @ds.sql
3444
3153
  ds.all
3445
3154
  end
3446
- @sqls.sort_by{|sql, s| s.to_s}.should == [['SELECT * FROM items', :default], ['SELECT * FROM items', :i], ['SELECT * FROM items', :s]]
3155
+ @db.sqls.sort.should == ['SELECT * FROM items', 'SELECT * FROM items -- i', 'SELECT * FROM items -- s']
3447
3156
  end
3448
3157
  end
3449
3158
 
@@ -3493,23 +3202,23 @@ end
3493
3202
 
3494
3203
  describe "Sequel::Dataset#qualify" do
3495
3204
  specify "should qualify to the given table" do
3496
- MockDatabase.new[:t].filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3205
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3497
3206
  end
3498
3207
 
3499
3208
  specify "should qualify to the first source if no table if given" do
3500
- MockDatabase.new[:t].filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
3209
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify.sql.should == 'SELECT t.* FROM t WHERE (t.a < t.b)'
3501
3210
  end
3502
3211
  end
3503
3212
 
3504
3213
  describe "Sequel::Dataset#qualify_to" do
3505
3214
  specify "should qualify to the given table" do
3506
- MockDatabase.new[:t].filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3215
+ Sequel::Dataset.new(nil).from(:t).filter{a<b}.qualify_to(:e).sql.should == 'SELECT e.* FROM t WHERE (e.a < e.b)'
3507
3216
  end
3508
3217
  end
3509
3218
 
3510
3219
  describe "Sequel::Dataset#qualify_to_first_source" do
3511
3220
  before do
3512
- @ds = MockDatabase.new[:t]
3221
+ @ds = Sequel::Database.new[:t]
3513
3222
  end
3514
3223
 
3515
3224
  specify "should qualify_to the first source" do
@@ -3519,13 +3228,11 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3519
3228
  end
3520
3229
 
3521
3230
  specify "should handle the select, order, where, having, and group options/clauses" do
3522
- @ds.select(:a).filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
3523
- 'SELECT t.a FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3231
+ @ds.select(:a).filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == 'SELECT t.a FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3524
3232
  end
3525
3233
 
3526
3234
  specify "should handle the select using a table.* if all columns are currently selected" do
3527
- @ds.filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == \
3528
- 'SELECT t.* FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3235
+ @ds.filter(:a=>1).order(:a).group(:a).having(:a).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a = 1) GROUP BY t.a HAVING t.a ORDER BY t.a'
3529
3236
  end
3530
3237
 
3531
3238
  specify "should handle hashes in select option" do
@@ -3594,14 +3301,83 @@ describe "Sequel::Dataset#qualify_to_first_source" do
3594
3301
  end
3595
3302
 
3596
3303
  specify "should handle all other objects by returning them unchanged" do
3597
- @ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('a > ?'.lit(1)).having(false).qualify_to_first_source.sql.should == \
3598
- "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
3304
+ @ds.select("a").filter{a(3)}.filter('blah').order('true'.lit).group('a > ?'.lit(1)).having(false).qualify_to_first_source.sql.should == "SELECT 'a' FROM t WHERE (a(3) AND (blah)) GROUP BY a > 1 HAVING 'f' ORDER BY true"
3305
+ end
3306
+ end
3307
+
3308
+ describe "Sequel::Dataset#unbind" do
3309
+ before do
3310
+ @ds = Sequel::Database.new[:t]
3311
+ @u = proc{|ds| ds, bv = ds.unbind; [ds.sql, bv]}
3312
+ end
3313
+
3314
+ specify "should unbind values assigned to equality and inequality statements" do
3315
+ @ds.filter(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo = $foo)"
3316
+ @ds.exclude(:foo=>1).unbind.first.sql.should == "SELECT * FROM t WHERE (foo != $foo)"
3317
+ @ds.filter{foo > 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo > $foo)"
3318
+ @ds.filter{foo >= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo >= $foo)"
3319
+ @ds.filter{foo < 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo < $foo)"
3320
+ @ds.filter{foo <= 1}.unbind.first.sql.should == "SELECT * FROM t WHERE (foo <= $foo)"
3321
+ end
3322
+
3323
+ specify "should return variables that could be used bound to recreate the previous query" do
3324
+ @ds.filter(:foo=>1).unbind.last.should == {:foo=>1}
3325
+ @ds.exclude(:foo=>1).unbind.last.should == {:foo=>1}
3326
+ end
3327
+
3328
+ specify "should handle numerics, strings, dates, times, and datetimes" do
3329
+ @u[@ds.filter(:foo=>1)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1}]
3330
+ @u[@ds.filter(:foo=>1.0)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>1.0}]
3331
+ @u[@ds.filter(:foo=>BigDecimal.new('1.0'))].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>BigDecimal.new('1.0')}]
3332
+ @u[@ds.filter(:foo=>'a')].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>'a'}]
3333
+ @u[@ds.filter(:foo=>Date.today)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>Date.today}]
3334
+ t = Time.now
3335
+ @u[@ds.filter(:foo=>t)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>t}]
3336
+ dt = DateTime.now
3337
+ @u[@ds.filter(:foo=>dt)].should == ["SELECT * FROM t WHERE (foo = $foo)", {:foo=>dt}]
3338
+ end
3339
+
3340
+ specify "should not unbind literal strings" do
3341
+ @u[@ds.filter(:foo=>'a'.lit)].should == ["SELECT * FROM t WHERE (foo = a)", {}]
3342
+ end
3343
+
3344
+ specify "should not unbind Identifiers, QualifiedIdentifiers, or Symbols used as booleans" do
3345
+ @u[@ds.filter(:foo).filter{bar}.filter{foo__bar}].should == ["SELECT * FROM t WHERE (foo AND bar AND foo.bar)", {}]
3346
+ end
3347
+
3348
+ specify "should not unbind for values it doesn't understand" do
3349
+ @u[@ds.filter(:foo=>Class.new{def sql_literal(ds) 'bar' end}.new)].should == ["SELECT * FROM t WHERE (foo = bar)", {}]
3350
+ end
3351
+
3352
+ specify "should handle QualifiedIdentifiers" do
3353
+ @u[@ds.filter{foo__bar > 1}].should == ["SELECT * FROM t WHERE (foo.bar > $foo.bar)", {:"foo.bar"=>1}]
3354
+ end
3355
+
3356
+ specify "should handle deep nesting" do
3357
+ @u[@ds.filter{foo > 1}.and{bar < 2}.or(:baz=>3).and({~{:x=>4}=>true}.case(false))].should == ["SELECT * FROM t WHERE ((((foo > $foo) AND (bar < $bar)) OR (baz = $baz)) AND (CASE WHEN (x != $x) THEN 't' ELSE 'f' END))", {:foo=>1, :bar=>2, :baz=>3, :x=>4}]
3358
+ end
3359
+
3360
+ specify "should handle JOIN ON" do
3361
+ @u[@ds.cross_join(:x).join(:a, [:u]).join(:b, [[:c, :d], [:e,1]])].should == ["SELECT * FROM t CROSS JOIN x INNER JOIN a USING (u) INNER JOIN b ON ((b.c = a.d) AND (b.e = $b.e))", {:"b.e"=>1}]
3362
+ end
3363
+
3364
+ specify "should raise an UnbindDuplicate exception if same variable is used with multiple different values" do
3365
+ proc{@ds.filter(:foo=>1).or(:foo=>2).unbind}.should raise_error(Sequel::UnbindDuplicate)
3366
+ end
3367
+
3368
+ specify "should handle case where the same variable has the same value in multiple places " do
3369
+ @u[@ds.filter(:foo=>1).or(:foo=>1)].should == ["SELECT * FROM t WHERE ((foo = $foo) OR (foo = $foo))", {:foo=>1}]
3370
+ end
3371
+
3372
+ specify "should raise Error for unhandled objects inside Identifiers and QualifiedIndentifiers" do
3373
+ proc{@ds.filter(Sequel::SQL::Identifier.new([]) > 1).unbind}.should raise_error(Sequel::Error)
3374
+ proc{@ds.filter{foo.qualify({}) > 1}.unbind}.should raise_error(Sequel::Error)
3599
3375
  end
3600
3376
  end
3601
3377
 
3602
3378
  describe "Sequel::Dataset #with and #with_recursive" do
3603
3379
  before do
3604
- @db = MockDatabase.new
3380
+ @db = Sequel::Database.new
3605
3381
  @ds = @db[:t]
3606
3382
  end
3607
3383
 
@@ -3633,11 +3409,27 @@ describe "Sequel::Dataset #with and #with_recursive" do
3633
3409
  proc{@ds.with(:t, @db[:x], :args=>[:b])}.should raise_error(Sequel::Error)
3634
3410
  proc{@ds.with_recursive(:t, @db[:x], @db[:t], :args=>[:b, :c])}.should raise_error(Sequel::Error)
3635
3411
  end
3412
+
3413
+ specify "#with should work on insert, update, and delete statements if they support it" do
3414
+ [:insert, :update, :delete].each do |m|
3415
+ @ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_with_sql"]}
3416
+ end
3417
+ @ds.with(:t, @db[:x]).insert_sql(1).should == 'WITH t AS (SELECT * FROM x) INSERT INTO t VALUES (1)'
3418
+ @ds.with(:t, @db[:x]).update_sql(:foo=>1).should == 'WITH t AS (SELECT * FROM x) UPDATE t SET foo = 1'
3419
+ @ds.with(:t, @db[:x]).delete_sql.should == 'WITH t AS (SELECT * FROM x) DELETE FROM t'
3420
+ end
3421
+
3422
+ specify "should hoist WITH clauses in given dataset(s) if dataset doesn't support WITH in subselect" do
3423
+ @ds.meta_def(:supports_cte?){true}
3424
+ @ds.meta_def(:supports_cte_in_subselect?){false}
3425
+ @ds.with(:t, @ds.from(:s).with(:s, @ds.from(:r))).sql.should == 'WITH s AS (SELECT * FROM r), t AS (SELECT * FROM s) SELECT * FROM t'
3426
+ @ds.with_recursive(:t, @ds.from(:s).with(:s, @ds.from(:r)), @ds.from(:q).with(:q, @ds.from(:p))).sql.should == 'WITH s AS (SELECT * FROM r), q AS (SELECT * FROM p), t AS (SELECT * FROM s UNION ALL SELECT * FROM q) SELECT * FROM t'
3427
+ end
3636
3428
  end
3637
3429
 
3638
3430
  describe Sequel::SQL::Constants do
3639
3431
  before do
3640
- @db = MockDatabase.new
3432
+ @db = Sequel::Database.new
3641
3433
  end
3642
3434
 
3643
3435
  it "should have CURRENT_DATE" do
@@ -3682,7 +3474,7 @@ end
3682
3474
 
3683
3475
  describe "Sequel timezone support" do
3684
3476
  before do
3685
- @db = MockDatabase.new
3477
+ @db = Sequel::Database.new
3686
3478
  @dataset = @db.dataset
3687
3479
  @dataset.meta_def(:supports_timestamp_timezones?){true}
3688
3480
  @dataset.meta_def(:supports_timestamp_usecs?){false}
@@ -3717,6 +3509,30 @@ describe "Sequel timezone support" do
3717
3509
  @dataset.literal(t).should == "#{s}#{@offset}'"
3718
3510
  end
3719
3511
 
3512
+ specify "should have Database#timezone override Sequel.database_timezone" do
3513
+ Sequel.database_timezone = :local
3514
+ @db.timezone = :utc
3515
+
3516
+ t = Time.now
3517
+ s = t.getutc.strftime("'%Y-%m-%d %H:%M:%S")
3518
+ @dataset.literal(t).should == "#{s}+0000'"
3519
+
3520
+ t = DateTime.now
3521
+ s = t.new_offset(0).strftime("'%Y-%m-%d %H:%M:%S")
3522
+ @dataset.literal(t).should == "#{s}+0000'"
3523
+
3524
+ Sequel.database_timezone = :utc
3525
+ @db.timezone = :local
3526
+
3527
+ t = Time.now.utc
3528
+ s = t.getlocal.strftime("'%Y-%m-%d %H:%M:%S")
3529
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3530
+
3531
+ t = DateTime.now.new_offset(0)
3532
+ s = t.new_offset(DateTime.now.offset).strftime("'%Y-%m-%d %H:%M:%S")
3533
+ @dataset.literal(t).should == "#{s}#{@offset}'"
3534
+ end
3535
+
3720
3536
  specify "should handle converting database timestamps into application timestamps" do
3721
3537
  Sequel.database_timezone = :utc
3722
3538
  Sequel.application_timezone = :local
@@ -3827,13 +3643,7 @@ end
3827
3643
 
3828
3644
  describe "Sequel::Dataset#select_map" do
3829
3645
  before do
3830
- @ds = MockDatabase.new[:t]
3831
- def @ds.fetch_rows(sql)
3832
- db << sql
3833
- yield({:c=>1})
3834
- yield({:c=>2})
3835
- end
3836
- @ds.db.reset
3646
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3837
3647
  end
3838
3648
 
3839
3649
  specify "should do select and map in one step" do
@@ -3860,17 +3670,23 @@ describe "Sequel::Dataset#select_map" do
3860
3670
  @ds.select_map{a(t__c)}.should == [1, 2]
3861
3671
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t']
3862
3672
  end
3673
+
3674
+ specify "should handle an array of columns" do
3675
+ @ds.select_map([:c, :c]).should == [[1, 1], [2, 2]]
3676
+ @ds.db.sqls.should == ['SELECT c, c FROM t']
3677
+ @ds.select_map([:d.as(:c), :c.qualify(:b), :c.identifier, :c.identifier.qualify(:b), :a__c, :a__d___c]).should == [[1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2]]
3678
+ @ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, a.c, a.d AS c FROM t']
3679
+ end
3680
+
3681
+ specify "should handle an array with a single element" do
3682
+ @ds.select_map([:c]).should == [[1], [2]]
3683
+ @ds.db.sqls.should == ['SELECT c FROM t']
3684
+ end
3863
3685
  end
3864
3686
 
3865
3687
  describe "Sequel::Dataset#select_order_map" do
3866
3688
  before do
3867
- @ds = MockDatabase.new[:t]
3868
- def @ds.fetch_rows(sql)
3869
- db << sql
3870
- yield({:c=>1})
3871
- yield({:c=>2})
3872
- end
3873
- @ds.db.reset
3689
+ @ds = Sequel.mock(:fetch=>[{:c=>1}, {:c=>2}])[:t]
3874
3690
  end
3875
3691
 
3876
3692
  specify "should do select and map in one step" do
@@ -3898,55 +3714,89 @@ describe "Sequel::Dataset#select_order_map" do
3898
3714
  @ds.db.sqls.should == ['SELECT a AS b FROM t ORDER BY a']
3899
3715
  end
3900
3716
 
3717
+ specify "should handle OrderedExpressions" do
3718
+ @ds.select_order_map(:a.desc).should == [1, 2]
3719
+ @ds.db.sqls.should == ['SELECT a FROM t ORDER BY a DESC']
3720
+ end
3721
+
3901
3722
  specify "should accept a block" do
3902
3723
  @ds.select_order_map{a(t__c)}.should == [1, 2]
3903
3724
  @ds.db.sqls.should == ['SELECT a(t.c) FROM t ORDER BY a(t.c)']
3904
3725
  end
3726
+
3727
+ specify "should handle an array of columns" do
3728
+ @ds.select_order_map([:c, :c]).should == [[1, 1], [2, 2]]
3729
+ @ds.db.sqls.should == ['SELECT c, c FROM t ORDER BY c, c']
3730
+ @ds.select_order_map([:d.as(:c), :c.qualify(:b), :c.identifier, :c.identifier.qualify(:b), :c.identifier.qualify(:b).desc, :a__c, :a__d___c.desc]).should == [[1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2]]
3731
+ @ds.db.sqls.should == ['SELECT d AS c, b.c, c, b.c, b.c, a.c, a.d AS c FROM t ORDER BY d, b.c, c, b.c, b.c DESC, a.c, a.d DESC']
3732
+ end
3733
+
3734
+ specify "should handle an array with a single element" do
3735
+ @ds.select_order_map([:c]).should == [[1], [2]]
3736
+ @ds.db.sqls.should == ['SELECT c FROM t ORDER BY c']
3737
+ end
3905
3738
  end
3906
3739
 
3907
3740
  describe "Sequel::Dataset#select_hash" do
3908
3741
  before do
3909
- @ds = MockDatabase.new[:t]
3910
- def @ds.set_fr_yield(hs)
3911
- @hs = hs
3912
- end
3913
- def @ds.fetch_rows(sql)
3914
- db << sql
3915
- @hs.each{|h| yield h}
3916
- end
3917
- @ds.db.reset
3742
+ @db = Sequel.mock(:fetch=>[{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3743
+ @ds = @db[:t]
3918
3744
  end
3919
3745
 
3920
- specify "should do select and map in one step" do
3921
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3746
+ specify "should do select and to_hash in one step" do
3922
3747
  @ds.select_hash(:a, :b).should == {1=>2, 3=>4}
3923
3748
  @ds.db.sqls.should == ['SELECT a, b FROM t']
3924
3749
  end
3925
3750
 
3926
3751
  specify "should handle implicit qualifiers in arguments" do
3927
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3928
3752
  @ds.select_hash(:t__a, :t__b).should == {1=>2, 3=>4}
3929
3753
  @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
3930
3754
  end
3931
3755
 
3932
3756
  specify "should handle implicit aliases in arguments" do
3933
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3934
3757
  @ds.select_hash(:c___a, :d___b).should == {1=>2, 3=>4}
3935
3758
  @ds.db.sqls.should == ['SELECT c AS a, d AS b FROM t']
3936
3759
  end
3937
3760
 
3938
3761
  specify "should handle implicit qualifiers and aliases in arguments" do
3939
- @ds.set_fr_yield([{:a=>1, :b=>2}, {:a=>3, :b=>4}])
3940
3762
  @ds.select_hash(:t__c___a, :t__d___b).should == {1=>2, 3=>4}
3941
3763
  @ds.db.sqls.should == ['SELECT t.c AS a, t.d AS b FROM t']
3942
3764
  end
3765
+
3766
+ specify "should handle SQL::Identifiers in arguments" do
3767
+ @ds.select_hash(:a.identifier, :b.identifier).should == {1=>2, 3=>4}
3768
+ @ds.db.sqls.should == ['SELECT a, b FROM t']
3769
+ end
3770
+
3771
+ specify "should handle SQL::QualifiedIdentifiers in arguments" do
3772
+ @ds.select_hash(:a.qualify(:t), :b.identifier.qualify(:t)).should == {1=>2, 3=>4}
3773
+ @ds.db.sqls.should == ['SELECT t.a, t.b FROM t']
3774
+ end
3775
+
3776
+ specify "should handle SQL::AliasedExpressions in arguments" do
3777
+ @ds.select_hash(:c.as(:a), :t.as(:b)).should == {1=>2, 3=>4}
3778
+ @ds.db.sqls.should == ['SELECT c AS a, t AS b FROM t']
3779
+ end
3780
+
3781
+ specify "should work with arrays of columns" do
3782
+ @db.fetch = [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}]
3783
+ @ds.select_hash([:a, :c], :b).should == {[1, 3]=>2, [4, 6]=>5}
3784
+ @ds.db.sqls.should == ['SELECT a, c, b FROM t']
3785
+ @ds.select_hash(:a, [:b, :c]).should == {1=>[2, 3], 4=>[5, 6]}
3786
+ @ds.db.sqls.should == ['SELECT a, b, c FROM t']
3787
+ @ds.select_hash([:a, :b], [:b, :c]).should == {[1, 2]=>[2, 3], [4, 5]=>[5, 6]}
3788
+ @ds.db.sqls.should == ['SELECT a, b, b, c FROM t']
3789
+ end
3790
+
3791
+ specify "should raise an error if the resulting symbol cannot be determined" do
3792
+ proc{@ds.select_hash(:c.as(:a), 'foo')}.should raise_error(Sequel::Error)
3793
+ end
3943
3794
  end
3944
3795
 
3945
3796
  describe "Modifying joined datasets" do
3946
3797
  before do
3947
- @ds = MockDatabase.new.from(:b, :c).join(:d, [:id]).where(:id => 2)
3798
+ @ds = Sequel.mock.from(:b, :c).join(:d, [:id]).where(:id => 2)
3948
3799
  @ds.meta_def(:supports_modifying_joins?){true}
3949
- @ds.db.reset
3950
3800
  end
3951
3801
 
3952
3802
  specify "should allow deleting from joined datasets" do
@@ -3962,7 +3812,7 @@ end
3962
3812
 
3963
3813
  describe "Dataset#lock_style and for_update" do
3964
3814
  before do
3965
- @ds = MockDatabase.new[:t]
3815
+ @ds = Sequel::Dataset.new(nil).from(:t)
3966
3816
  end
3967
3817
 
3968
3818
  specify "#for_update should use FOR UPDATE" do
@@ -3977,3 +3827,89 @@ describe "Dataset#lock_style and for_update" do
3977
3827
  @ds.lock_style("FOR SHARE").sql.should == "SELECT * FROM t FOR SHARE"
3978
3828
  end
3979
3829
  end
3830
+
3831
+ describe "Custom ASTTransformer" do
3832
+ specify "should transform given objects" do
3833
+ c = Class.new(Sequel::ASTTransformer) do
3834
+ def v(s)
3835
+ (s.is_a?(Symbol) || s.is_a?(String)) ? :"#{s}#{s}" : super
3836
+ end
3837
+ end.new
3838
+ ds = Sequel::Dataset.new(nil).from(:t).cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
3839
+ ds.sql.should == 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
3840
+ ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.should == 'SELECT * FROM tt CROSS JOIN aa AS gg INNER JOIN bb AS hh USING (cc) INNER JOIN dd AS ii ON (ii.ee = hh.ff)'
3841
+ end
3842
+ end
3843
+
3844
+ describe "Dataset#returning" do
3845
+ before do
3846
+ @ds = Sequel.mock(:fetch=>proc{|s| {:foo=>s}})[:t].returning(:foo)
3847
+ @pr = proc do
3848
+ [:insert, :update, :delete].each do |m|
3849
+ @ds.meta_def(:"#{m}_clause_methods"){super() + [:"#{m}_returning_sql"]}
3850
+ end
3851
+ end
3852
+ end
3853
+
3854
+ specify "should use RETURNING clause in the SQL if the dataset supports it" do
3855
+ @pr.call
3856
+ @ds.delete_sql.should == "DELETE FROM t RETURNING foo"
3857
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1) RETURNING foo"
3858
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1 RETURNING foo"
3859
+ end
3860
+
3861
+ specify "should not use RETURNING clause in the SQL if the dataset does not support it" do
3862
+ @ds.delete_sql.should == "DELETE FROM t"
3863
+ @ds.insert_sql(1).should == "INSERT INTO t VALUES (1)"
3864
+ @ds.update_sql(:foo=>1).should == "UPDATE t SET foo = 1"
3865
+ end
3866
+
3867
+ specify "should have insert, update, and delete yield to blocks if RETURNING is used" do
3868
+ @pr.call
3869
+ h = {}
3870
+ @ds.delete{|r| h = r}
3871
+ h.should == {:foo=>"DELETE FROM t RETURNING foo"}
3872
+ @ds.insert(1){|r| h = r}
3873
+ h.should == {:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}
3874
+ @ds.update(:foo=>1){|r| h = r}
3875
+ h.should == {:foo=>"UPDATE t SET foo = 1 RETURNING foo"}
3876
+ end
3877
+
3878
+ specify "should have insert, update, and delete return arrays of hashes if RETURNING is used and a block is not given" do
3879
+ @pr.call
3880
+ h = {}
3881
+ @ds.delete.should == [{:foo=>"DELETE FROM t RETURNING foo"}]
3882
+ @ds.insert(1).should == [{:foo=>"INSERT INTO t VALUES (1) RETURNING foo"}]
3883
+ @ds.update(:foo=>1).should == [{:foo=>"UPDATE t SET foo = 1 RETURNING foo"}]
3884
+ end
3885
+ end
3886
+
3887
+ describe "Dataset emulating bitwise operator support" do
3888
+ before do
3889
+ @ds = Sequel::Database.new.dataset
3890
+ @ds.quote_identifiers = true
3891
+ def @ds.complex_expression_sql(op, args)
3892
+ complex_expression_arg_pairs(args){|a, b| "bitand(#{literal(a)}, #{literal(b)})"}
3893
+ end
3894
+ end
3895
+
3896
+ it "should work with any numbers of arguments for operators" do
3897
+ @ds.select(Sequel::SQL::ComplexExpression.new(:&, :x)).sql.should == 'SELECT "x"'
3898
+ @ds.select(:x & 1).sql.should == 'SELECT bitand("x", 1)'
3899
+ @ds.select(:x & 1 & 2).sql.should == 'SELECT bitand(bitand("x", 1), 2)'
3900
+ end
3901
+ end
3902
+
3903
+ describe "Dataset feature defaults" do
3904
+ it "should not require aliases for recursive CTEs by default" do
3905
+ Sequel::Database.new.dataset.recursive_cte_requires_column_aliases?.should be_false
3906
+ end
3907
+
3908
+ it "should not require placeholder type specifiers by default" do
3909
+ Sequel::Database.new.dataset.requires_placeholder_type_specifiers?.should be_false
3910
+ end
3911
+
3912
+ it "offset use should be returning a separate row number column by default" do
3913
+ Sequel::Database.new.dataset.send(:offset_returns_row_number_column?).should be_false
3914
+ end
3915
+ end