sequel 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/CHANGELOG +68 -0
  2. data/COPYING +1 -1
  3. data/README.rdoc +87 -27
  4. data/bin/sequel +2 -4
  5. data/doc/association_basics.rdoc +1383 -0
  6. data/doc/dataset_basics.rdoc +106 -0
  7. data/doc/opening_databases.rdoc +45 -16
  8. data/doc/querying.rdoc +210 -0
  9. data/doc/release_notes/3.11.0.txt +254 -0
  10. data/doc/virtual_rows.rdoc +217 -31
  11. data/lib/sequel/adapters/ado.rb +28 -12
  12. data/lib/sequel/adapters/ado/mssql.rb +33 -1
  13. data/lib/sequel/adapters/amalgalite.rb +13 -8
  14. data/lib/sequel/adapters/db2.rb +1 -2
  15. data/lib/sequel/adapters/dbi.rb +7 -4
  16. data/lib/sequel/adapters/do.rb +14 -15
  17. data/lib/sequel/adapters/do/postgres.rb +4 -5
  18. data/lib/sequel/adapters/do/sqlite.rb +9 -0
  19. data/lib/sequel/adapters/firebird.rb +5 -10
  20. data/lib/sequel/adapters/informix.rb +2 -4
  21. data/lib/sequel/adapters/jdbc.rb +111 -49
  22. data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
  24. data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
  27. data/lib/sequel/adapters/mysql.rb +14 -5
  28. data/lib/sequel/adapters/odbc.rb +2 -4
  29. data/lib/sequel/adapters/odbc/mssql.rb +2 -4
  30. data/lib/sequel/adapters/openbase.rb +1 -2
  31. data/lib/sequel/adapters/oracle.rb +4 -8
  32. data/lib/sequel/adapters/postgres.rb +4 -11
  33. data/lib/sequel/adapters/shared/mssql.rb +22 -9
  34. data/lib/sequel/adapters/shared/mysql.rb +33 -30
  35. data/lib/sequel/adapters/shared/oracle.rb +0 -5
  36. data/lib/sequel/adapters/shared/postgres.rb +13 -11
  37. data/lib/sequel/adapters/shared/sqlite.rb +56 -10
  38. data/lib/sequel/adapters/sqlite.rb +16 -9
  39. data/lib/sequel/connection_pool.rb +6 -1
  40. data/lib/sequel/connection_pool/single.rb +1 -0
  41. data/lib/sequel/core.rb +6 -1
  42. data/lib/sequel/database.rb +52 -23
  43. data/lib/sequel/database/schema_generator.rb +6 -0
  44. data/lib/sequel/database/schema_methods.rb +5 -5
  45. data/lib/sequel/database/schema_sql.rb +1 -1
  46. data/lib/sequel/dataset.rb +4 -190
  47. data/lib/sequel/dataset/actions.rb +323 -1
  48. data/lib/sequel/dataset/features.rb +18 -2
  49. data/lib/sequel/dataset/graph.rb +7 -0
  50. data/lib/sequel/dataset/misc.rb +119 -0
  51. data/lib/sequel/dataset/mutation.rb +64 -0
  52. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  53. data/lib/sequel/dataset/query.rb +272 -6
  54. data/lib/sequel/dataset/sql.rb +186 -394
  55. data/lib/sequel/model.rb +4 -2
  56. data/lib/sequel/model/associations.rb +31 -14
  57. data/lib/sequel/model/base.rb +32 -13
  58. data/lib/sequel/model/exceptions.rb +8 -4
  59. data/lib/sequel/model/plugins.rb +3 -13
  60. data/lib/sequel/plugins/active_model.rb +26 -7
  61. data/lib/sequel/plugins/instance_filters.rb +98 -0
  62. data/lib/sequel/plugins/many_through_many.rb +1 -1
  63. data/lib/sequel/plugins/optimistic_locking.rb +25 -9
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +26 -0
  66. data/spec/adapters/mysql_spec.rb +33 -4
  67. data/spec/adapters/postgres_spec.rb +24 -1
  68. data/spec/adapters/spec_helper.rb +6 -0
  69. data/spec/adapters/sqlite_spec.rb +28 -0
  70. data/spec/core/connection_pool_spec.rb +17 -5
  71. data/spec/core/database_spec.rb +101 -1
  72. data/spec/core/dataset_spec.rb +42 -4
  73. data/spec/core/schema_spec.rb +13 -0
  74. data/spec/extensions/active_model_spec.rb +34 -11
  75. data/spec/extensions/caching_spec.rb +2 -0
  76. data/spec/extensions/instance_filters_spec.rb +55 -0
  77. data/spec/extensions/spec_helper.rb +2 -0
  78. data/spec/integration/dataset_test.rb +12 -1
  79. data/spec/integration/model_test.rb +12 -0
  80. data/spec/integration/plugin_test.rb +61 -1
  81. data/spec/integration/schema_test.rb +14 -3
  82. data/spec/model/base_spec.rb +27 -0
  83. data/spec/model/plugins_spec.rb +0 -22
  84. data/spec/model/record_spec.rb +32 -1
  85. data/spec/model/spec_helper.rb +2 -0
  86. metadata +14 -3
  87. data/lib/sequel/dataset/convenience.rb +0 -326
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  MAJOR = 3
3
- MINOR = 10
3
+ MINOR = 11
4
4
  TINY = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, TINY].join('.')
@@ -375,3 +375,29 @@ context "MSSSQL::Dataset#into" do
375
375
  @db.drop_table(:new)
376
376
  end
377
377
  end
378
+
379
+ context "A MSSQL database" do
380
+ before do
381
+ @db = MSSQL_DB
382
+ end
383
+ after do
384
+ @db.drop_table(:a)
385
+ end
386
+
387
+ specify "should handle many existing types for set_column_allow_null" do
388
+ @db.create_table!(:a){column :a, 'integer'}
389
+ @db.alter_table(:a){set_column_allow_null :a, false}
390
+ @db.create_table!(:a){column :a, 'decimal(24, 2)'}
391
+ @db.alter_table(:a){set_column_allow_null :a, false}
392
+ @db.schema(:a).first.last[:column_size].should == 24
393
+ @db.schema(:a).first.last[:scale].should == 2
394
+ @db.create_table!(:a){column :a, 'decimal(10)'}
395
+ @db.schema(:a).first.last[:column_size].should == 10
396
+ @db.schema(:a).first.last[:scale].should == 0
397
+ @db.alter_table(:a){set_column_allow_null :a, false}
398
+ @db.create_table!(:a){column :a, 'nchar(2)'}
399
+ @db.alter_table(:a){set_column_allow_null :a, false}
400
+ s = @db.schema(:a).first.last
401
+ (s[:max_chars] || s[:column_size]).should == 2
402
+ end
403
+ end
@@ -107,14 +107,20 @@ if MYSQL_DB.class.adapter_scheme == :mysql
107
107
  @db.schema(:booltest, :reload=>true).should == [[:b, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(1)"}, ], [:i, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :ruby_default=>nil, :db_type=>"tinyint(4)"}, ]]
108
108
  end
109
109
 
110
- specify "should return tinyints as bools when set" do
110
+ specify "should return tinyint(1)s as bools and tinyint(4)s as integers when set" do
111
+ Sequel::MySQL.convert_tinyint_to_bool = true
111
112
  @ds.delete
112
113
  @ds << {:b=>true, :i=>10}
113
- @ds.all.should == [{:b=>true, :i=>true}]
114
+ @ds.all.should == [{:b=>true, :i=>10}]
114
115
  @ds.delete
115
116
  @ds << {:b=>false, :i=>0}
116
- @ds.all.should == [{:b=>false, :i=>false}]
117
-
117
+ @ds.all.should == [{:b=>false, :i=>0}]
118
+ @ds.delete
119
+ @ds << {:b=>true, :i=>1}
120
+ @ds.all.should == [{:b=>true, :i=>1}]
121
+ end
122
+
123
+ specify "should return all tinyints as integers when unset" do
118
124
  Sequel::MySQL.convert_tinyint_to_bool = false
119
125
  @ds.delete
120
126
  @ds << {:b=>true, :i=>10}
@@ -244,6 +250,29 @@ context "MySQL datasets" do
244
250
  end
245
251
  end
246
252
 
253
+ describe "Dataset#distinct" do
254
+ before do
255
+ @db = MYSQL_DB
256
+ @db.create_table!(:a) do
257
+ Integer :a
258
+ Integer :b
259
+ end
260
+ @ds = @db[:a]
261
+ end
262
+ after do
263
+ @db.drop_table(:a)
264
+ end
265
+
266
+ it "#distinct with arguments should return results distinct on those arguments" do
267
+ @ds.insert(20, 10)
268
+ @ds.insert(30, 10)
269
+ @ds.order(:b, :a).distinct.map(:a).should == [20, 30]
270
+ @ds.order(:b, :a.desc).distinct.map(:a).should == [30, 20]
271
+ # MySQL doesn't respect orders when using the nonstandard GROUP BY
272
+ [[20], [30]].should include(@ds.order(:b, :a).distinct(:b).map(:a))
273
+ end
274
+ end
275
+
247
276
  context "MySQL join expressions" do
248
277
  before do
249
278
  @ds = MYSQL_DB[:nodes]
@@ -148,6 +148,29 @@ context "A PostgreSQL dataset" do
148
148
  end
149
149
  end
150
150
 
151
+ describe "Dataset#distinct" do
152
+ before do
153
+ @db = POSTGRES_DB
154
+ @db.create_table!(:a) do
155
+ Integer :a
156
+ Integer :b
157
+ end
158
+ @ds = @db[:a]
159
+ end
160
+ after do
161
+ @db.drop_table(:a)
162
+ end
163
+
164
+ it "#distinct with arguments should return results distinct on those arguments" do
165
+ @ds.insert(20, 10)
166
+ @ds.insert(30, 10)
167
+ @ds.order(:b, :a).distinct.map(:a).should == [20, 30]
168
+ @ds.order(:b, :a.desc).distinct.map(:a).should == [30, 20]
169
+ @ds.order(:b, :a).distinct(:b).map(:a).should == [20]
170
+ @ds.order(:b, :a.desc).distinct(:b).map(:a).should == [30]
171
+ end
172
+ end
173
+
151
174
  if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
152
175
  describe "Dataset#for_update support" do
153
176
  before do
@@ -194,7 +217,7 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
194
217
  c = @ds.for_share.filter(:id=>1).first
195
218
  end
196
219
  end
197
- sleep 0.05
220
+ sleep 0.1
198
221
  @ds.filter(:id=>1).update(:name=>'Jim')
199
222
  c.should == {:id=>1, :number=>20, :name=>nil}
200
223
  end
@@ -10,6 +10,12 @@ begin
10
10
  rescue LoadError
11
11
  end
12
12
 
13
+ class Sequel::Database
14
+ def log_duration(duration, message)
15
+ log_info(message)
16
+ end
17
+ end
18
+
13
19
  class Spec::Example::ExampleGroup
14
20
  def log
15
21
  begin
@@ -36,6 +36,34 @@ context "An SQLite database" do
36
36
  end
37
37
  end
38
38
 
39
+ specify "should provide the SQLite version as an integer" do
40
+ @db.sqlite_version.should be_a_kind_of(Integer)
41
+ end
42
+
43
+ specify "should support setting and getting the foreign_keys pragma" do
44
+ (@db.sqlite_version >= 30619 ? [true, false] : [nil]).should include(@db.foreign_keys)
45
+ @db.foreign_keys = true
46
+ @db.foreign_keys = false
47
+ end
48
+
49
+ if SQLITE_DB.sqlite_version >= 30619
50
+ specify "should enforce foreign key integrity if foreign_keys pragma is set" do
51
+ @db.foreign_keys = true
52
+ @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
53
+ @db[:fk].insert(1, nil)
54
+ @db[:fk].insert(2, 1)
55
+ @db[:fk].insert(3, 3)
56
+ proc{@db[:fk].insert(4, 5)}.should raise_error(Sequel::Error)
57
+ end
58
+ end
59
+
60
+ specify "should not enforce foreign key integrity if foreign_keys pragma is unset" do
61
+ @db.foreign_keys = false
62
+ @db.create_table!(:fk){primary_key :id; foreign_key :parent_id, :fk}
63
+ @db[:fk].insert(1, 2)
64
+ @db[:fk].all.should == [{:id=>1, :parent_id=>2}]
65
+ end
66
+
39
67
  specify "should provide a list of existing tables" do
40
68
  @db.drop_table(:testing) rescue nil
41
69
  @db.tables.should be_a_kind_of(Array)
@@ -240,8 +240,8 @@ shared_examples_for "A threaded connection pool" do
240
240
  threads = []
241
241
  stop = nil
242
242
 
243
- 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.01;end}}; sleep 0.01}
244
- sleep 0.02
243
+ 5.times {|i| threads << Thread.new {@pool.hold {|c| cc[i] = c; while !stop;sleep 0.02;end}}; sleep 0.02}
244
+ sleep 0.04
245
245
  threads.each {|t| t.should be_alive}
246
246
  cc.size.should == 5
247
247
  @invoked_count.should == 5
@@ -253,16 +253,16 @@ shared_examples_for "A threaded connection pool" do
253
253
  @pool.allocated.should == h
254
254
 
255
255
  threads[0].raise "your'e dead"
256
- sleep 0.01
256
+ sleep 0.02
257
257
  threads[3].raise "your'e dead too"
258
258
 
259
- sleep 0.01
259
+ sleep 0.02
260
260
 
261
261
  @pool.available_connections.should == [1, 4]
262
262
  @pool.allocated.should == {threads[1]=>2, threads[2]=>3, threads[4]=>5}
263
263
 
264
264
  stop = true
265
- sleep 0.02
265
+ sleep 0.04
266
266
 
267
267
  @pool.available_connections.size.should == 5
268
268
  @pool.allocated.should be_empty
@@ -734,6 +734,12 @@ context "A single threaded pool with multiple servers" do
734
734
  end
735
735
 
736
736
  shared_examples_for "All connection pools classes" do
737
+ specify "should not raise an error when disconnecting twice" do
738
+ c = @class.new({}){123}
739
+ proc{c.disconnect}.should_not raise_error
740
+ proc{c.disconnect}.should_not raise_error
741
+ end
742
+
737
743
  specify "should yield a connection created by the initialize block to hold" do
738
744
  x = nil
739
745
  @class.new({}){123}.hold{|c| x = c}
@@ -746,6 +752,12 @@ shared_examples_for "All connection pools classes" do
746
752
  x.should == [:default, :default]
747
753
  end
748
754
 
755
+ specify "should have respect an :after_connect proc that is called with each newly created connection" do
756
+ x = nil
757
+ @class.new(:after_connect=>proc{|c| x = [c, c]}){|c| 123}.hold{}
758
+ x.should == [123, 123]
759
+ end
760
+
749
761
  specify "should raise a DatabaseConnectionError if the connection raises an exception" do
750
762
  proc{@class.new({}){|c| raise Exception}.hold{}}.should raise_error(Sequel::DatabaseConnectionError)
751
763
  end
@@ -209,6 +209,88 @@ context "Database#connect" do
209
209
  end
210
210
  end
211
211
 
212
+ context "Database#log_info" do
213
+ before do
214
+ @o = Object.new
215
+ def @o.logs; @logs || []; end
216
+ def @o.method_missing(*args); (@logs ||= []) << args; end
217
+ @db = Sequel::Database.new(:logger=>@o)
218
+ end
219
+
220
+ specify "should log message at info level to all loggers" do
221
+ @db.log_info('blah')
222
+ @o.logs.should == [[:info, 'blah']]
223
+ end
224
+
225
+ specify "should log message with args at info level to all loggers" do
226
+ @db.log_info('blah', [1, 2])
227
+ @o.logs.should == [[:info, 'blah; [1, 2]']]
228
+ end
229
+ end
230
+
231
+ context "Database#log_yield" do
232
+ before do
233
+ @o = Object.new
234
+ def @o.logs; @logs || []; end
235
+ def @o.warn(*args); (@logs ||= []) << [:warn] + args; end
236
+ def @o.method_missing(*args); (@logs ||= []) << args; end
237
+ @db = Sequel::Database.new(:logger=>@o)
238
+ end
239
+
240
+ specify "should yield to the passed block" do
241
+ a = nil
242
+ @db.log_yield('blah'){a = 1}
243
+ a.should == 1
244
+ end
245
+
246
+ specify "should raise an exception if a block is not passed" do
247
+ proc{@db.log_yield('blah')}.should raise_error
248
+ end
249
+
250
+ specify "should log message with duration at info level to all loggers" do
251
+ @db.log_yield('blah'){}
252
+ @o.logs.length.should == 1
253
+ @o.logs.first.length.should == 2
254
+ @o.logs.first.first.should == :info
255
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
256
+ end
257
+
258
+ specify "should log message with duration at warn level if duration greater than log_warn_duration" do
259
+ @db.log_warn_duration = 0
260
+ @db.log_yield('blah'){}
261
+ @o.logs.length.should == 1
262
+ @o.logs.first.length.should == 2
263
+ @o.logs.first.first.should == :warn
264
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
265
+ end
266
+
267
+ specify "should log message with duration at info level if duration less than log_warn_duration" do
268
+ @db.log_warn_duration = 1000
269
+ @db.log_yield('blah'){}
270
+ @o.logs.length.should == 1
271
+ @o.logs.first.length.should == 2
272
+ @o.logs.first.first.should == :info
273
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah\z/
274
+ end
275
+
276
+ specify "should log message at error level if block raises an error" do
277
+ @db.log_warn_duration = 0
278
+ proc{@db.log_yield('blah'){raise Sequel::Error, 'adsf'}}.should raise_error
279
+ @o.logs.length.should == 1
280
+ @o.logs.first.length.should == 2
281
+ @o.logs.first.first.should == :error
282
+ @o.logs.first.last.should =~ /\ASequel::Error: adsf: blah\z/
283
+ end
284
+
285
+ specify "should include args with message if args passed" do
286
+ @db.log_yield('blah', [1, 2]){}
287
+ @o.logs.length.should == 1
288
+ @o.logs.first.length.should == 2
289
+ @o.logs.first.first.should == :info
290
+ @o.logs.first.last.should =~ /\A\(\d\.\d{6}s\) blah; \[1, 2\]\z/
291
+ end
292
+ end
293
+
212
294
  context "Database#uri" do
213
295
  before do
214
296
  @c = Class.new(Sequel::Database) do
@@ -342,7 +424,7 @@ context "Database#test_connection" do
342
424
  @db = Sequel::Database.new{@test = rand(100)}
343
425
  end
344
426
 
345
- specify "should call pool#hold" do
427
+ specify "should pool#hold" do
346
428
  @db.test_connection
347
429
  @test.should_not be_nil
348
430
  end
@@ -350,6 +432,10 @@ context "Database#test_connection" do
350
432
  specify "should return true if successful" do
351
433
  @db.test_connection.should be_true
352
434
  end
435
+
436
+ specify "should raise an error if the attempting to connect raises an error" do
437
+ proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
438
+ end
353
439
  end
354
440
 
355
441
  class DummyDataset < Sequel::Dataset
@@ -875,6 +961,20 @@ context "A Database adapter with a scheme" do
875
961
  c.opts[:database].should == 'd[b]'
876
962
  c.opts[:host].should == 'domain\\instance'
877
963
  end
964
+
965
+ specify "should test the connection if test parameter is truthy" do
966
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=t'}.should raise_error(Sequel::DatabaseConnectionError)
967
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=1'}.should raise_error(Sequel::DatabaseConnectionError)
968
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>true}.should raise_error(Sequel::DatabaseConnectionError)
969
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>'t'}.should raise_error(Sequel::DatabaseConnectionError)
970
+ end
971
+
972
+ specify "should not test the connection if test parameter is not truthy" do
973
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=f'}.should_not raise_error
974
+ proc{Sequel.connect 'ccc:///d%5bb%5d?test=0'}.should_not raise_error
975
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>false}.should_not raise_error
976
+ proc{Sequel.connect 'ccc:///d%5bb%5d', :test=>'f'}.should_not raise_error
977
+ end
878
978
  end
879
979
 
880
980
  context "Sequel::Database.connect" do
@@ -322,6 +322,16 @@ context "Dataset#where" do
322
322
  @d3 = @dataset.where("a = 1")
323
323
  end
324
324
 
325
+ specify "should just clone if given an empty argument" do
326
+ @dataset.where({}).sql.should == @dataset.sql
327
+ @dataset.where([]).sql.should == @dataset.sql
328
+ @dataset.where('').sql.should == @dataset.sql
329
+
330
+ @dataset.filter({}).sql.should == @dataset.sql
331
+ @dataset.filter([]).sql.should == @dataset.sql
332
+ @dataset.filter('').sql.should == @dataset.sql
333
+ end
334
+
325
335
  specify "should work with hashes" do
326
336
  @dataset.where(:name => 'xyz', :price => 342).select_sql.
327
337
  should match(/WHERE \(\(name = 'xyz'\) AND \(price = 342\)\)|WHERE \(\(price = 342\) AND \(name = 'xyz'\)\)/)
@@ -735,6 +745,12 @@ context "Dataset#having" do
735
745
  @columns = "region, sum(population), avg(gdp)"
736
746
  end
737
747
 
748
+ specify "should just clone if given an empty argument" do
749
+ @dataset.having({}).sql.should == @dataset.sql
750
+ @dataset.having([]).sql.should == @dataset.sql
751
+ @dataset.having('').sql.should == @dataset.sql
752
+ end
753
+
738
754
  specify "should affect select statements" do
739
755
  @d1.select_sql.should ==
740
756
  "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
@@ -1151,6 +1167,28 @@ context "Dataset#select_more" do
1151
1167
  end
1152
1168
  end
1153
1169
 
1170
+ context "Dataset#select_append" do
1171
+ before do
1172
+ @d = Sequel::Dataset.new(nil).from(:test)
1173
+ end
1174
+
1175
+ specify "should select * in addition to columns if no columns selected" do
1176
+ @d.select_append(:a, :b).sql.should == 'SELECT *, a, b FROM test'
1177
+ @d.select_all.select_append(:a, :b).sql.should == 'SELECT *, a, b FROM test'
1178
+ @d.select(:blah).select_all.select_append(:a, :b).sql.should == 'SELECT *, a, b FROM test'
1179
+ end
1180
+
1181
+ specify "should add to the currently selected columns" do
1182
+ @d.select(:a).select_append(:b).sql.should == 'SELECT a, b FROM test'
1183
+ @d.select(:a.*).select_append(:b.*).sql.should == 'SELECT a.*, b.* FROM test'
1184
+ end
1185
+
1186
+ specify "should accept a block that yields a virtual row" do
1187
+ @d.select(:a).select_append{|o| o.b}.sql.should == 'SELECT a, b FROM test'
1188
+ @d.select(:a.*).select_append(:b.*){b(1)}.sql.should == 'SELECT a.*, b.*, b(1) FROM test'
1189
+ end
1190
+ end
1191
+
1154
1192
  context "Dataset#order" do
1155
1193
  before do
1156
1194
  @dataset = Sequel::Dataset.new(nil).from(:test)
@@ -1484,13 +1522,13 @@ context "Dataset#distinct" do
1484
1522
  @dataset.distinct.sql.should == 'SELECT DISTINCT name FROM test'
1485
1523
  end
1486
1524
 
1487
- specify "should raise an error if columns given and distinct on not supported" do
1488
- @dataset.meta_def(:supports_distinct_on?){false}
1525
+ specify "should raise an error if columns given and DISTINCT ON is not supported" do
1489
1526
  proc{@dataset.distinct}.should_not raise_error
1490
1527
  proc{@dataset.distinct(:a)}.should raise_error(Sequel::InvalidOperation)
1491
1528
  end
1492
1529
 
1493
- specify "should accept an expression list" do
1530
+ specify "should use DISTINCT ON if columns are given and DISTINCT ON is supported" do
1531
+ @dataset.meta_def(:supports_distinct_on?){true}
1494
1532
  @dataset.distinct(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
1495
1533
  @dataset.distinct(:stamp.cast(:integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (CAST(stamp AS integer), (node_id IS NULL)) name FROM test'
1496
1534
  end
@@ -2541,7 +2579,7 @@ context "Dataset#columns" do
2541
2579
  end
2542
2580
 
2543
2581
  specify "should ignore any filters, orders, or DISTINCT clauses" do
2544
- @dataset.filter!(:b=>100).order!(:b).distinct!(:b)
2582
+ @dataset.filter!(:b=>100).order!(:b).distinct!
2545
2583
  @dataset.columns = nil
2546
2584
  @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2547
2585
  end