sequel 3.5.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +108 -0
- data/README.rdoc +25 -14
- data/Rakefile +20 -1
- data/doc/advanced_associations.rdoc +61 -64
- data/doc/cheat_sheet.rdoc +16 -7
- data/doc/opening_databases.rdoc +3 -3
- data/doc/prepared_statements.rdoc +1 -1
- data/doc/reflection.rdoc +2 -1
- data/doc/release_notes/3.6.0.txt +366 -0
- data/doc/schema.rdoc +19 -14
- data/lib/sequel/adapters/amalgalite.rb +5 -27
- data/lib/sequel/adapters/jdbc.rb +13 -3
- data/lib/sequel/adapters/jdbc/h2.rb +17 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +20 -7
- data/lib/sequel/adapters/mysql.rb +4 -3
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +87 -28
- data/lib/sequel/adapters/shared/mssql.rb +47 -6
- data/lib/sequel/adapters/shared/mysql.rb +12 -31
- data/lib/sequel/adapters/shared/postgres.rb +15 -12
- data/lib/sequel/adapters/shared/sqlite.rb +18 -0
- data/lib/sequel/adapters/sqlite.rb +1 -16
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +2 -0
- data/lib/sequel/database/schema_sql.rb +1 -1
- data/lib/sequel/dataset.rb +5 -179
- data/lib/sequel/dataset/actions.rb +123 -0
- data/lib/sequel/dataset/convenience.rb +18 -10
- data/lib/sequel/dataset/features.rb +65 -0
- data/lib/sequel/dataset/prepared_statements.rb +29 -23
- data/lib/sequel/dataset/query.rb +429 -0
- data/lib/sequel/dataset/sql.rb +67 -435
- data/lib/sequel/model/associations.rb +77 -13
- data/lib/sequel/model/base.rb +30 -8
- data/lib/sequel/model/errors.rb +4 -4
- data/lib/sequel/plugins/caching.rb +38 -15
- data/lib/sequel/plugins/force_encoding.rb +18 -7
- data/lib/sequel/plugins/hook_class_methods.rb +4 -0
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +40 -11
- data/lib/sequel/plugins/serialization.rb +17 -3
- data/lib/sequel/plugins/validation_helpers.rb +65 -18
- data/lib/sequel/sql.rb +23 -1
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +96 -10
- data/spec/adapters/mysql_spec.rb +19 -0
- data/spec/adapters/postgres_spec.rb +65 -2
- data/spec/adapters/sqlite_spec.rb +10 -0
- data/spec/core/core_sql_spec.rb +9 -0
- data/spec/core/database_spec.rb +8 -4
- data/spec/core/dataset_spec.rb +122 -29
- data/spec/core/expression_filters_spec.rb +17 -0
- data/spec/extensions/caching_spec.rb +43 -3
- data/spec/extensions/force_encoding_spec.rb +43 -1
- data/spec/extensions/nested_attributes_spec.rb +55 -2
- data/spec/extensions/validation_helpers_spec.rb +71 -0
- data/spec/integration/associations_test.rb +281 -0
- data/spec/integration/dataset_test.rb +383 -9
- data/spec/integration/eager_loader_test.rb +0 -65
- data/spec/integration/model_test.rb +110 -0
- data/spec/integration/plugin_test.rb +306 -0
- data/spec/integration/prepared_statement_test.rb +32 -0
- data/spec/integration/schema_test.rb +8 -3
- data/spec/integration/spec_helper.rb +1 -25
- data/spec/model/association_reflection_spec.rb +38 -0
- data/spec/model/associations_spec.rb +184 -8
- data/spec/model/eager_loading_spec.rb +23 -0
- data/spec/model/model_spec.rb +8 -0
- data/spec/model/record_spec.rb +84 -1
- metadata +9 -2
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -787,6 +787,25 @@ context "MySQL::Dataset#replace" do
|
|
787
787
|
MYSQL_DB.drop_table(:items)
|
788
788
|
end
|
789
789
|
|
790
|
+
specify "should use default values if they exist" do
|
791
|
+
MYSQL_DB.alter_table(:items){set_column_default :id, 1; set_column_default :value, 2}
|
792
|
+
@d.replace
|
793
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
794
|
+
@d.replace([])
|
795
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
796
|
+
@d.replace({})
|
797
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
798
|
+
end
|
799
|
+
|
800
|
+
specify "should use support arrays, datasets, and multiple values" do
|
801
|
+
@d.replace([1, 2])
|
802
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
803
|
+
@d.replace(1, 2)
|
804
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
805
|
+
@d.replace(@d)
|
806
|
+
@d.all.should == [{:id=>1, :value=>2}]
|
807
|
+
end
|
808
|
+
|
790
809
|
specify "should create a record if the condition is not met" do
|
791
810
|
@d.replace(:id => 111, :value => 333)
|
792
811
|
@d.all.should == [{:id => 111, :value => 333}]
|
@@ -129,6 +129,24 @@ context "A PostgreSQL dataset" do
|
|
129
129
|
@d.filter(:name => /bc/).count.should == 2
|
130
130
|
@d.filter(:name => /^bc/).count.should == 1
|
131
131
|
end
|
132
|
+
|
133
|
+
specify "should support for_share and for_update" do
|
134
|
+
@d.for_share.all.should == []
|
135
|
+
@d.for_update.all.should == []
|
136
|
+
end
|
137
|
+
|
138
|
+
specify "#lock should lock tables and yield if a block is given" do
|
139
|
+
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
|
140
|
+
end
|
141
|
+
|
142
|
+
specify "#lock should lock table if inside a transaction" do
|
143
|
+
POSTGRES_DB.transaction{@d.lock('EXCLUSIVE'); @d.insert(:name=>'a')}
|
144
|
+
end
|
145
|
+
|
146
|
+
specify "#lock should return nil" do
|
147
|
+
@d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}.should == nil
|
148
|
+
POSTGRES_DB.transaction{@d.lock('EXCLUSIVE').should == nil; @d.insert(:name=>'a')}
|
149
|
+
end
|
132
150
|
end
|
133
151
|
|
134
152
|
context "A PostgreSQL dataset with a timestamp field" do
|
@@ -189,6 +207,11 @@ context "A PostgreSQL database" do
|
|
189
207
|
|
190
208
|
@db[:test2].first[:xyz].should == 57
|
191
209
|
end
|
210
|
+
|
211
|
+
specify "#locks should be a dataset returning database locks " do
|
212
|
+
@db.locks.should be_a_kind_of(Sequel::Dataset)
|
213
|
+
@db.locks.all.should be_a_kind_of(Array)
|
214
|
+
end
|
192
215
|
end
|
193
216
|
|
194
217
|
context "A PostgreSQL database" do
|
@@ -217,7 +240,6 @@ context "A PostgreSQL database" do
|
|
217
240
|
@db.reset_primary_key_sequence(:posts).should == nil
|
218
241
|
end
|
219
242
|
|
220
|
-
|
221
243
|
specify "should support opclass specification" do
|
222
244
|
@db.create_table(:posts){text :title; text :body; integer :user_id; index(:user_id, :opclass => :int4_ops, :type => :btree)}
|
223
245
|
@db.sqls.should == [
|
@@ -287,6 +309,11 @@ context "A PostgreSQL database" do
|
|
287
309
|
"CREATE INDEX posts_title_index ON posts (title) WHERE (title = '5')"
|
288
310
|
]
|
289
311
|
end
|
312
|
+
|
313
|
+
specify "should support renaming tables" do
|
314
|
+
@db.create_table!(:posts1){primary_key :a}
|
315
|
+
@db.rename_table(:posts1, :posts)
|
316
|
+
end
|
290
317
|
end
|
291
318
|
|
292
319
|
context "Postgres::Dataset#import" do
|
@@ -393,7 +420,7 @@ context "Postgres::Dataset#insert" do
|
|
393
420
|
@ds.disable_insert_returning.insert_select(:value=>10).should == nil
|
394
421
|
end
|
395
422
|
|
396
|
-
specify "should have insert_select insert the record and return the inserted record if server_version
|
423
|
+
specify "should have insert_select insert the record and return the inserted record if server_version >= 80200" do
|
397
424
|
@ds.meta_def(:server_version){80201}
|
398
425
|
h = @ds.insert_select(:value=>10)
|
399
426
|
h[:value].should == 10
|
@@ -635,3 +662,39 @@ context "Postgres::Database functions, languages, and triggers" do
|
|
635
662
|
@d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
|
636
663
|
end
|
637
664
|
end
|
665
|
+
|
666
|
+
if POSTGRES_DB.class.adapter_scheme == :postgres
|
667
|
+
context "Postgres::Dataset #use_cursor" do
|
668
|
+
before(:all) do
|
669
|
+
@db = POSTGRES_DB
|
670
|
+
@db.create_table!(:test_cursor){Integer :x}
|
671
|
+
@db.sqls.clear
|
672
|
+
@ds = @db[:test_cursor]
|
673
|
+
@db.transaction{1001.times{|i| @ds.insert(i)}}
|
674
|
+
end
|
675
|
+
after(:all) do
|
676
|
+
@db.drop_table(:test) rescue nil
|
677
|
+
end
|
678
|
+
|
679
|
+
specify "should return the same results as the non-cursor use" do
|
680
|
+
@ds.all.should == @ds.use_cursor.all
|
681
|
+
end
|
682
|
+
|
683
|
+
specify "should respect the :rows_per_fetch option" do
|
684
|
+
@db.sqls.clear
|
685
|
+
@ds.use_cursor.all
|
686
|
+
@db.sqls.length.should == 6
|
687
|
+
@db.sqls.clear
|
688
|
+
@ds.use_cursor(:rows_per_fetch=>100).all
|
689
|
+
@db.sqls.length.should == 15
|
690
|
+
end
|
691
|
+
|
692
|
+
specify "should handle returning inside block" do
|
693
|
+
def @ds.check_return
|
694
|
+
use_cursor.each{|r| return}
|
695
|
+
end
|
696
|
+
@ds.check_return
|
697
|
+
@ds.all.should == @ds.use_cursor.all
|
698
|
+
end
|
699
|
+
end
|
700
|
+
end
|
@@ -241,6 +241,16 @@ context "SQLite dataset" do
|
|
241
241
|
SQLITE_DB[:test].select(:name, :value).order(:value).to_a.should == \
|
242
242
|
@d.select(:name, :value).order(:value).to_a
|
243
243
|
end
|
244
|
+
|
245
|
+
specify "should support #explain" do
|
246
|
+
SQLITE_DB[:test].explain.should be_a_kind_of(Array)
|
247
|
+
end
|
248
|
+
|
249
|
+
specify "should have #explain work when identifier_output_method is modified" do
|
250
|
+
ds = SQLITE_DB[:test]
|
251
|
+
ds.identifier_output_method = :upcase
|
252
|
+
ds.explain.should be_a_kind_of(Array)
|
253
|
+
end
|
244
254
|
end
|
245
255
|
|
246
256
|
context "A SQLite database" do
|
data/spec/core/core_sql_spec.rb
CHANGED
@@ -82,6 +82,15 @@ context "String#lit" do
|
|
82
82
|
ds.quote_identifiers = true
|
83
83
|
ds.literal(a).should == 'DISTINCT "a"'
|
84
84
|
end
|
85
|
+
|
86
|
+
specify "should handle named placeholders if given a single argument hash" do
|
87
|
+
a = 'DISTINCT :b'.lit(:b=>:a)
|
88
|
+
a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
|
89
|
+
ds = MockDatabase.new.dataset
|
90
|
+
ds.literal(a).should == 'DISTINCT a'
|
91
|
+
ds.quote_identifiers = true
|
92
|
+
ds.literal(a).should == 'DISTINCT "a"'
|
93
|
+
end
|
85
94
|
end
|
86
95
|
|
87
96
|
context "String#to_sequel_blob" do
|
data/spec/core/database_spec.rb
CHANGED
@@ -1027,12 +1027,16 @@ context "Database#fetch" do
|
|
1027
1027
|
@db.fetch('select * from xyz where x = ? and y = ?', 15, 'abc') {|r| sql = r[:sql]}
|
1028
1028
|
sql.should == "select * from xyz where x = 15 and y = 'abc'"
|
1029
1029
|
|
1030
|
-
|
1031
|
-
@db.fetch('select name from table where name = ? or id in ?',
|
1032
|
-
'aman', [3,4,7]) {|r| sql = r[:sql]}
|
1030
|
+
@db.fetch('select name from table where name = ? or id in ?', 'aman', [3,4,7]) {|r| sql = r[:sql]}
|
1033
1031
|
sql.should == "select name from table where name = 'aman' or id in (3, 4, 7)"
|
1034
1032
|
end
|
1035
1033
|
|
1034
|
+
specify "should format the given sql with named arguments" do
|
1035
|
+
sql = nil
|
1036
|
+
@db.fetch('select * from xyz where x = :x and y = :y', :x=>15, :y=>'abc') {|r| sql = r[:sql]}
|
1037
|
+
sql.should == "select * from xyz where x = 15 and y = 'abc'"
|
1038
|
+
end
|
1039
|
+
|
1036
1040
|
specify "should return the dataset if no block is given" do
|
1037
1041
|
@db.fetch('select * from xyz').should be_a_kind_of(Sequel::Dataset)
|
1038
1042
|
|
@@ -1065,7 +1069,7 @@ context "Database#[]" do
|
|
1065
1069
|
ds.opts[:from].should == [:items]
|
1066
1070
|
end
|
1067
1071
|
|
1068
|
-
specify "should return
|
1072
|
+
specify "should return a dataset when a string is given" do
|
1069
1073
|
c = Class.new(Sequel::Dataset) do
|
1070
1074
|
def fetch_rows(sql); yield({:sql => sql}); end
|
1071
1075
|
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -300,15 +300,44 @@ context "Dataset#where" do
|
|
300
300
|
should match(/WHERE \(\(name = 'xyz'\) AND \(price = 342\)\)|WHERE \(\(price = 342\) AND \(name = 'xyz'\)\)/)
|
301
301
|
end
|
302
302
|
|
303
|
-
specify "should work with
|
303
|
+
specify "should work with a string with placeholders and arguments for those placeholders" do
|
304
304
|
@dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.should ==
|
305
305
|
"SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
|
306
306
|
end
|
307
307
|
|
308
|
+
specify "should not modify passed array with placeholders" do
|
309
|
+
a = ['price < ? AND id in ?', 100, 1, 2, 3]
|
310
|
+
b = a.dup
|
311
|
+
@dataset.where(a)
|
312
|
+
b.should == a
|
313
|
+
end
|
314
|
+
|
308
315
|
specify "should work with strings (custom SQL expressions)" do
|
309
316
|
@dataset.where('(a = 1 AND b = 2)').select_sql.should ==
|
310
317
|
"SELECT * FROM test WHERE ((a = 1 AND b = 2))"
|
311
318
|
end
|
319
|
+
|
320
|
+
specify "should work with a string with named placeholders and a hash of placeholder value arguments" do
|
321
|
+
@dataset.where('price < :price AND id in :ids', :price=>100, :ids=>[1, 2, 3]).select_sql.should ==
|
322
|
+
"SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
|
323
|
+
end
|
324
|
+
|
325
|
+
specify "should not modify passed array with named placeholders" do
|
326
|
+
a = ['price < :price AND id in :ids', {:price=>100}]
|
327
|
+
b = a.dup
|
328
|
+
@dataset.where(a)
|
329
|
+
b.should == a
|
330
|
+
end
|
331
|
+
|
332
|
+
specify "should not replace named placeholders that don't existin in the hash" do
|
333
|
+
@dataset.where('price < :price AND id in :ids', :price=>100).select_sql.should ==
|
334
|
+
"SELECT * FROM test WHERE (price < 100 AND id in :ids)"
|
335
|
+
end
|
336
|
+
|
337
|
+
specify "should handle partial names" do
|
338
|
+
@dataset.where('price < :price AND id = :p', :p=>2, :price=>100).select_sql.should ==
|
339
|
+
"SELECT * FROM test WHERE (price < 100 AND id = 2)"
|
340
|
+
end
|
312
341
|
|
313
342
|
specify "should affect select, delete and update statements" do
|
314
343
|
@d1.select_sql.should == "SELECT * FROM test WHERE (region = 'Asia')"
|
@@ -626,10 +655,6 @@ context "Dataset#having" do
|
|
626
655
|
@columns = "region, sum(population), avg(gdp)"
|
627
656
|
end
|
628
657
|
|
629
|
-
specify "should raise if the dataset is not grouped" do
|
630
|
-
proc {@dataset.having('avg(gdp) > 10')}.should raise_error(Sequel::InvalidOperation)
|
631
|
-
end
|
632
|
-
|
633
658
|
specify "should affect select statements" do
|
634
659
|
@d1.select_sql.should ==
|
635
660
|
"SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
|
@@ -768,8 +793,20 @@ context "Dataset#literal" do
|
|
768
793
|
@dataset.literal(:items__name).should == "items.name"
|
769
794
|
end
|
770
795
|
|
771
|
-
specify "should
|
772
|
-
|
796
|
+
specify "should call sql_literal with dataset on type if not natively supported and the object responds to it" do
|
797
|
+
@a = Class.new do
|
798
|
+
def sql_literal(ds)
|
799
|
+
"called #{ds.blah}"
|
800
|
+
end
|
801
|
+
end
|
802
|
+
def @dataset.blah
|
803
|
+
"ds"
|
804
|
+
end
|
805
|
+
@dataset.literal(@a.new).should == "called ds"
|
806
|
+
end
|
807
|
+
|
808
|
+
specify "should raise an error for unsupported types with no sql_literal method" do
|
809
|
+
proc {@dataset.literal(Object.new)}.should raise_error
|
773
810
|
end
|
774
811
|
|
775
812
|
specify "should literalize datasets as subqueries" do
|
@@ -1106,10 +1143,18 @@ context "Dataset#with_sql" do
|
|
1106
1143
|
@dataset = Sequel::Dataset.new(nil).from(:test)
|
1107
1144
|
end
|
1108
1145
|
|
1109
|
-
specify "should
|
1146
|
+
specify "should use static sql" do
|
1110
1147
|
@dataset.with_sql('SELECT 1 FROM test').sql.should == 'SELECT 1 FROM test'
|
1111
1148
|
end
|
1112
1149
|
|
1150
|
+
specify "should work with placeholders" do
|
1151
|
+
@dataset.with_sql('SELECT ? FROM test', 1).sql.should == 'SELECT 1 FROM test'
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
specify "should work with named placeholders" do
|
1155
|
+
@dataset.with_sql('SELECT :x FROM test', :x=>1).sql.should == 'SELECT 1 FROM test'
|
1156
|
+
end
|
1157
|
+
|
1113
1158
|
specify "should keep row_proc" do
|
1114
1159
|
@dataset.with_sql('SELECT 1 FROM test').row_proc.should == @dataset.row_proc
|
1115
1160
|
end
|
@@ -1440,6 +1485,11 @@ context "Dataset#group_and_count" do
|
|
1440
1485
|
@ds.group_and_count(:a, :b).sql.should ==
|
1441
1486
|
"SELECT a, b, count(*) AS count FROM test GROUP BY a, b ORDER BY count"
|
1442
1487
|
end
|
1488
|
+
|
1489
|
+
specify "should format column aliases in the select clause but not in the group clause" do
|
1490
|
+
@ds.group_and_count(:name___n).sql.should ==
|
1491
|
+
"SELECT name AS n, count(*) AS count FROM test GROUP BY name ORDER BY count"
|
1492
|
+
end
|
1443
1493
|
end
|
1444
1494
|
|
1445
1495
|
context "Dataset#empty?" do
|
@@ -1511,38 +1561,54 @@ context "Dataset#join_table" do
|
|
1511
1561
|
'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
|
1512
1562
|
end
|
1513
1563
|
|
1514
|
-
specify "should support
|
1515
|
-
@d.join_table(:
|
1516
|
-
'SELECT * FROM "items"
|
1564
|
+
specify "should support arbitrary join types" do
|
1565
|
+
@d.join_table(:magic, :categories, :category_id=>:id).sql.should ==
|
1566
|
+
'SELECT * FROM "items" MAGIC JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1567
|
+
end
|
1517
1568
|
|
1569
|
+
specify "should support many join methods" do
|
1518
1570
|
@d.left_outer_join(:categories, :category_id=>:id).sql.should ==
|
1519
1571
|
'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1520
|
-
end
|
1521
|
-
|
1522
|
-
specify "should support right outer joins" do
|
1523
|
-
@d.join_table(:right_outer, :categories, :category_id=>:id).sql.should ==
|
1524
|
-
'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1525
|
-
|
1526
1572
|
@d.right_outer_join(:categories, :category_id=>:id).sql.should ==
|
1527
1573
|
'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1528
|
-
end
|
1529
|
-
|
1530
|
-
specify "should support full outer joins" do
|
1531
|
-
@d.join_table(:full_outer, :categories, :category_id=>:id).sql.should ==
|
1532
|
-
'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1533
|
-
|
1534
1574
|
@d.full_outer_join(:categories, :category_id=>:id).sql.should ==
|
1535
1575
|
'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1536
|
-
end
|
1537
|
-
|
1538
|
-
specify "should support inner joins" do
|
1539
|
-
@d.join_table(:inner, :categories, :category_id=>:id).sql.should ==
|
1540
|
-
'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1541
|
-
|
1542
1576
|
@d.inner_join(:categories, :category_id=>:id).sql.should ==
|
1543
1577
|
'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1578
|
+
@d.left_join(:categories, :category_id=>:id).sql.should ==
|
1579
|
+
'SELECT * FROM "items" LEFT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1580
|
+
@d.right_join(:categories, :category_id=>:id).sql.should ==
|
1581
|
+
'SELECT * FROM "items" RIGHT JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1582
|
+
@d.full_join(:categories, :category_id=>:id).sql.should ==
|
1583
|
+
'SELECT * FROM "items" FULL JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
1584
|
+
@d.natural_join(:categories).sql.should ==
|
1585
|
+
'SELECT * FROM "items" NATURAL JOIN "categories"'
|
1586
|
+
@d.natural_left_join(:categories).sql.should ==
|
1587
|
+
'SELECT * FROM "items" NATURAL LEFT JOIN "categories"'
|
1588
|
+
@d.natural_right_join(:categories).sql.should ==
|
1589
|
+
'SELECT * FROM "items" NATURAL RIGHT JOIN "categories"'
|
1590
|
+
@d.natural_full_join(:categories).sql.should ==
|
1591
|
+
'SELECT * FROM "items" NATURAL FULL JOIN "categories"'
|
1592
|
+
@d.cross_join(:categories).sql.should ==
|
1593
|
+
'SELECT * FROM "items" CROSS JOIN "categories"'
|
1544
1594
|
end
|
1545
1595
|
|
1596
|
+
specify "should raise an error if additional arguments are provided to join methods that don't take conditions" do
|
1597
|
+
proc{@d.natural_join(:categories, :id=>:id)}.should raise_error(ArgumentError)
|
1598
|
+
proc{@d.natural_left_join(:categories, :id=>:id)}.should raise_error(ArgumentError)
|
1599
|
+
proc{@d.natural_right_join(:categories, :id=>:id)}.should raise_error(ArgumentError)
|
1600
|
+
proc{@d.natural_full_join(:categories, :id=>:id)}.should raise_error(ArgumentError)
|
1601
|
+
proc{@d.cross_join(:categories, :id=>:id)}.should raise_error(ArgumentError)
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
specify "should raise an error if blocks are provided to join methods that don't pass them" do
|
1605
|
+
proc{@d.natural_join(:categories){}}.should raise_error(Sequel::Error)
|
1606
|
+
proc{@d.natural_left_join(:categories){}}.should raise_error(Sequel::Error)
|
1607
|
+
proc{@d.natural_right_join(:categories){}}.should raise_error(Sequel::Error)
|
1608
|
+
proc{@d.natural_full_join(:categories){}}.should raise_error(Sequel::Error)
|
1609
|
+
proc{@d.cross_join(:categories){}}.should raise_error(Sequel::Error)
|
1610
|
+
end
|
1611
|
+
|
1546
1612
|
specify "should default to a plain join if nil is used for the type" do
|
1547
1613
|
@d.join_table(nil, :categories, :category_id=>:id).sql.should ==
|
1548
1614
|
'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
|
@@ -1680,6 +1746,11 @@ context "Dataset#join_table" do
|
|
1680
1746
|
'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
|
1681
1747
|
end
|
1682
1748
|
|
1749
|
+
specify "should emulate JOIN USING (poorly) if the dataset doesn't support it" do
|
1750
|
+
@d.meta_def(:supports_join_using?){false}
|
1751
|
+
@d.join(:categories, [:id]).sql.should == 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."id" = "items"."id")'
|
1752
|
+
end
|
1753
|
+
|
1683
1754
|
specify "should raise an error if using an array of symbols with a block" do
|
1684
1755
|
proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
|
1685
1756
|
end
|
@@ -1852,6 +1923,14 @@ context "Dataset aggregate methods" do
|
|
1852
1923
|
specify "should accept qualified columns" do
|
1853
1924
|
@d.avg(:test__bc).should == 'SELECT avg(test.bc) FROM test LIMIT 1'
|
1854
1925
|
end
|
1926
|
+
|
1927
|
+
specify "should use a subselect for the same conditions as count" do
|
1928
|
+
d = @d.order(:a).limit(5)
|
1929
|
+
d.avg(:a).should == 'SELECT avg(a) FROM (SELECT * FROM test ORDER BY a LIMIT 5) AS t1 LIMIT 1'
|
1930
|
+
d.sum(:a).should == 'SELECT sum(a) FROM (SELECT * FROM test ORDER BY a LIMIT 5) AS t1 LIMIT 1'
|
1931
|
+
d.min(:a).should == 'SELECT min(a) FROM (SELECT * FROM test ORDER BY a LIMIT 5) AS t1 LIMIT 1'
|
1932
|
+
d.max(:a).should == 'SELECT max(a) FROM (SELECT * FROM test ORDER BY a LIMIT 5) AS t1 LIMIT 1'
|
1933
|
+
end
|
1855
1934
|
end
|
1856
1935
|
|
1857
1936
|
context "Dataset#range" do
|
@@ -1880,6 +1959,11 @@ context "Dataset#range" do
|
|
1880
1959
|
specify "should return a range object" do
|
1881
1960
|
@d.range(:tryme).should == (1..10)
|
1882
1961
|
end
|
1962
|
+
|
1963
|
+
specify "should use a subselect for the same conditions as count" do
|
1964
|
+
@d.order(:stamp).limit(5).range(:stamp).should == (1..10)
|
1965
|
+
@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'
|
1966
|
+
end
|
1883
1967
|
end
|
1884
1968
|
|
1885
1969
|
context "Dataset#interval" do
|
@@ -1908,6 +1992,11 @@ context "Dataset#interval" do
|
|
1908
1992
|
specify "should return an integer" do
|
1909
1993
|
@d.interval(:tryme).should == 1234
|
1910
1994
|
end
|
1995
|
+
|
1996
|
+
specify "should use a subselect for the same conditions as count" do
|
1997
|
+
@d.order(:stamp).limit(5).interval(:stamp).should == 1234
|
1998
|
+
@d.last_sql.should == 'SELECT (max(stamp) - min(stamp)) FROM (SELECT * FROM test ORDER BY stamp LIMIT 5) AS t1 LIMIT 1'
|
1999
|
+
end
|
1911
2000
|
end
|
1912
2001
|
|
1913
2002
|
context "Dataset #first and #last" do
|
@@ -3080,6 +3169,10 @@ context "Sequel::Dataset#qualify_to_first_source" do
|
|
3080
3169
|
@ds.filter('? > ?', :a, 1).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.a > 1)'
|
3081
3170
|
end
|
3082
3171
|
|
3172
|
+
specify "should handle SQL::PlaceholderLiteralStrings with named placeholders" do
|
3173
|
+
@ds.filter(':a > :b', :a=>:c, :b=>1).qualify_to_first_source.sql.should == 'SELECT t.* FROM t WHERE (t.c > 1)'
|
3174
|
+
end
|
3175
|
+
|
3083
3176
|
specify "should handle SQL::WindowFunctions" do
|
3084
3177
|
@ds.meta_def(:supports_window_functions?){true}
|
3085
3178
|
@ds.select{sum(:over, :args=>:a, :partition=>:b, :order=>:c){}}.qualify_to_first_source.sql.should == 'SELECT sum(t.a) OVER (PARTITION BY t.b ORDER BY t.c) FROM t'
|
@@ -327,6 +327,17 @@ context "Blockless Ruby Filters" do
|
|
327
327
|
@d.l([[:x, nil], [:y, [1,2,3]]].sql_or).should == '((x IS NULL) OR (y IN (1, 2, 3)))'
|
328
328
|
end
|
329
329
|
|
330
|
+
it "should emulate columns for array values" do
|
331
|
+
@d.l([:x, :y]=>[[1,2], [3,4]].sql_array).should == '((x, y) IN ((1, 2), (3, 4)))'
|
332
|
+
@d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '((x, y, z) IN ((1, 2, 5), (3, 4, 6)))'
|
333
|
+
end
|
334
|
+
|
335
|
+
it "should emulate multiple column in if not supported" do
|
336
|
+
@d.meta_def(:supports_multiple_column_in?){false}
|
337
|
+
@d.l([:x, :y]=>[[1,2], [3,4]].sql_array).should == '(((x = 1) AND (y = 2)) OR ((x = 3) AND (y = 4)))'
|
338
|
+
@d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '(((x = 1) AND (y = 2) AND (z = 5)) OR ((x = 3) AND (y = 4) AND (z = 6)))'
|
339
|
+
end
|
340
|
+
|
330
341
|
it "should support Array#sql_string_join for concatenation of SQL strings" do
|
331
342
|
@d.lit([:x].sql_string_join).should == '(x)'
|
332
343
|
@d.lit([:x].sql_string_join(', ')).should == '(x)'
|
@@ -533,4 +544,10 @@ context Sequel::SQL::VirtualRow do
|
|
533
544
|
proc{@d.l{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}}.should raise_error(Sequel::Error)
|
534
545
|
proc{Sequel::Dataset.new(nil).filter{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.sql}.should raise_error(Sequel::Error)
|
535
546
|
end
|
547
|
+
|
548
|
+
it "should deal with classes without requiring :: prefix" do
|
549
|
+
@d.l{date < Date.today}.should == "(\"date\" < '#{Date.today}')"
|
550
|
+
@d.l{date < Sequel::CURRENT_DATE}.should == "(\"date\" < CURRENT_DATE)"
|
551
|
+
@d.l{num < Math::PI.to_i}.should == "(\"num\" < 3)"
|
552
|
+
end
|
536
553
|
end
|