sequel 3.5.0 → 3.6.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.
- 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
|