sequel 4.8.0 → 4.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +48 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/opening_databases.rdoc +4 -0
- data/doc/postgresql.rdoc +27 -3
- data/doc/release_notes/4.9.0.txt +190 -0
- data/doc/security.rdoc +1 -1
- data/doc/testing.rdoc +2 -2
- data/doc/validations.rdoc +8 -0
- data/lib/sequel/adapters/jdbc.rb +5 -3
- data/lib/sequel/adapters/jdbc/derby.rb +2 -8
- data/lib/sequel/adapters/jdbc/h2.rb +2 -13
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -16
- data/lib/sequel/adapters/mysql2.rb +11 -1
- data/lib/sequel/adapters/postgres.rb +33 -10
- data/lib/sequel/adapters/shared/db2.rb +2 -10
- data/lib/sequel/adapters/shared/mssql.rb +10 -8
- data/lib/sequel/adapters/shared/oracle.rb +9 -24
- data/lib/sequel/adapters/shared/postgres.rb +32 -9
- data/lib/sequel/adapters/shared/sqlanywhere.rb +2 -4
- data/lib/sequel/adapters/shared/sqlite.rb +4 -7
- data/lib/sequel/database/schema_methods.rb +15 -0
- data/lib/sequel/dataset.rb +1 -1
- data/lib/sequel/dataset/actions.rb +159 -27
- data/lib/sequel/dataset/graph.rb +29 -7
- data/lib/sequel/dataset/misc.rb +6 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +164 -0
- data/lib/sequel/dataset/query.rb +2 -0
- data/lib/sequel/dataset/sql.rb +103 -91
- data/lib/sequel/extensions/current_datetime_timestamp.rb +57 -0
- data/lib/sequel/extensions/pg_array.rb +68 -106
- data/lib/sequel/extensions/pg_hstore.rb +5 -5
- data/lib/sequel/extensions/schema_dumper.rb +49 -49
- data/lib/sequel/model.rb +4 -2
- data/lib/sequel/model/associations.rb +1 -1
- data/lib/sequel/model/base.rb +136 -3
- data/lib/sequel/model/errors.rb +6 -0
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/eager_each.rb +9 -0
- data/lib/sequel/plugins/nested_attributes.rb +2 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/plugins/touch.rb +2 -2
- data/lib/sequel/sql.rb +20 -15
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +70 -8
- data/spec/core/dataset_spec.rb +172 -27
- data/spec/core/expression_filters_spec.rb +3 -3
- data/spec/core/object_graph_spec.rb +17 -1
- data/spec/core/placeholder_literalizer_spec.rb +128 -0
- data/spec/core/schema_spec.rb +54 -0
- data/spec/extensions/current_datetime_timestamp_spec.rb +27 -0
- data/spec/extensions/defaults_setter_spec.rb +12 -0
- data/spec/extensions/eager_each_spec.rb +6 -0
- data/spec/extensions/nested_attributes_spec.rb +14 -2
- data/spec/extensions/pg_array_spec.rb +15 -7
- data/spec/extensions/shared_caching_spec.rb +5 -5
- data/spec/extensions/timestamps_spec.rb +9 -0
- data/spec/extensions/touch_spec.rb +9 -0
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +27 -5
- data/spec/model/eager_loading_spec.rb +32 -0
- data/spec/model/model_spec.rb +119 -9
- metadata +8 -2
data/spec/core/dataset_spec.rb
CHANGED
@@ -3254,46 +3254,30 @@ describe "Dataset#grep" do
|
|
3254
3254
|
end
|
3255
3255
|
end
|
3256
3256
|
|
3257
|
-
describe "Dataset default #fetch_rows, #insert, #update, #delete, #
|
3257
|
+
describe "Dataset default #fetch_rows, #insert, #update, #delete, #truncate, #execute" do
|
3258
3258
|
before do
|
3259
|
-
@db = Sequel
|
3259
|
+
@db = Sequel.mock(:servers=>{:read_only=>{}}, :autoid=>1)
|
3260
3260
|
@ds = @db[:items]
|
3261
3261
|
end
|
3262
3262
|
|
3263
3263
|
specify "#delete should execute delete SQL" do
|
3264
|
-
@
|
3265
|
-
@
|
3266
|
-
@db.should_receive(:execute_dui).once.with('DELETE FROM items', :server=>:default)
|
3267
|
-
@ds.delete
|
3268
|
-
end
|
3269
|
-
|
3270
|
-
specify "#with_sql_delete should execute delete SQL" do
|
3271
|
-
sql = 'DELETE FROM foo'
|
3272
|
-
@db.should_receive(:execute).once.with(sql, :server=>:default)
|
3273
|
-
@ds.with_sql_delete(sql)
|
3274
|
-
@db.should_receive(:execute_dui).once.with(sql, :server=>:default)
|
3275
|
-
@ds.with_sql_delete(sql)
|
3264
|
+
@ds.delete.should == 0
|
3265
|
+
@db.sqls.should == ["DELETE FROM items"]
|
3276
3266
|
end
|
3277
3267
|
|
3278
3268
|
specify "#insert should execute insert SQL" do
|
3279
|
-
@
|
3280
|
-
@
|
3281
|
-
@db.should_receive(:execute_insert).once.with('INSERT INTO items DEFAULT VALUES', :server=>:default)
|
3282
|
-
@ds.insert([])
|
3269
|
+
@ds.insert([]).should == 1
|
3270
|
+
@db.sqls.should == ["INSERT INTO items DEFAULT VALUES"]
|
3283
3271
|
end
|
3284
3272
|
|
3285
3273
|
specify "#update should execute update SQL" do
|
3286
|
-
@
|
3287
|
-
@
|
3288
|
-
@db.should_receive(:execute_dui).once.with('UPDATE items SET number = 1', :server=>:default)
|
3289
|
-
@ds.update(:number=>1)
|
3274
|
+
@ds.update(:number=>1).should == 0
|
3275
|
+
@db.sqls.should == ["UPDATE items SET number = 1"]
|
3290
3276
|
end
|
3291
3277
|
|
3292
3278
|
specify "#truncate should execute truncate SQL" do
|
3293
|
-
@db.should_receive(:execute).once.with('TRUNCATE TABLE items', :server=>:default)
|
3294
|
-
@ds.truncate.should == nil
|
3295
|
-
@db.should_receive(:execute_ddl).once.with('TRUNCATE TABLE items', :server=>:default)
|
3296
3279
|
@ds.truncate.should == nil
|
3280
|
+
@db.sqls.should == ["TRUNCATE TABLE items"]
|
3297
3281
|
end
|
3298
3282
|
|
3299
3283
|
specify "#truncate should raise an InvalidOperation exception if the dataset is filtered" do
|
@@ -3302,8 +3286,71 @@ describe "Dataset default #fetch_rows, #insert, #update, #delete, #with_sql_dele
|
|
3302
3286
|
end
|
3303
3287
|
|
3304
3288
|
specify "#execute should execute the SQL on the database" do
|
3305
|
-
@db.should_receive(:execute).once.with('SELECT 1', :server=>:read_only)
|
3306
3289
|
@ds.send(:execute, 'SELECT 1')
|
3290
|
+
@db.sqls.should == ["SELECT 1 -- read_only"]
|
3291
|
+
end
|
3292
|
+
end
|
3293
|
+
|
3294
|
+
describe "Dataset#with_sql_*" do
|
3295
|
+
before do
|
3296
|
+
@db = Sequel.mock(:servers=>{:read_only=>{}}, :autoid=>1, :fetch=>{:id=>1})
|
3297
|
+
@ds = @db[:items]
|
3298
|
+
end
|
3299
|
+
|
3300
|
+
specify "#with_sql_insert should execute given insert SQL" do
|
3301
|
+
@ds.with_sql_insert('INSERT INTO foo (1)').should == 1
|
3302
|
+
@db.sqls.should == ["INSERT INTO foo (1)"]
|
3303
|
+
end
|
3304
|
+
|
3305
|
+
specify "#with_sql_delete should execute given delete SQL" do
|
3306
|
+
@ds.with_sql_delete('DELETE FROM foo').should == 0
|
3307
|
+
@db.sqls.should == ["DELETE FROM foo"]
|
3308
|
+
end
|
3309
|
+
|
3310
|
+
specify "#with_sql_update should execute given update SQL" do
|
3311
|
+
@ds.with_sql_update('UPDATE foo SET a = 1').should == 0
|
3312
|
+
@db.sqls.should == ["UPDATE foo SET a = 1"]
|
3313
|
+
end
|
3314
|
+
|
3315
|
+
specify "#with_sql_all should return all rows from running the SQL" do
|
3316
|
+
@ds.with_sql_all('SELECT * FROM foo').should == [{:id=>1}]
|
3317
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3318
|
+
end
|
3319
|
+
|
3320
|
+
specify "#with_sql_all should yield each row to the block" do
|
3321
|
+
a = []
|
3322
|
+
@ds.with_sql_all('SELECT * FROM foo'){|r| a << r}
|
3323
|
+
a.should == [{:id=>1}]
|
3324
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3325
|
+
end
|
3326
|
+
|
3327
|
+
specify "#with_sql_each should yield each row to the block" do
|
3328
|
+
a = []
|
3329
|
+
@ds.with_sql_each('SELECT * FROM foo'){|r| a << r}
|
3330
|
+
a.should == [{:id=>1}]
|
3331
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3332
|
+
end
|
3333
|
+
|
3334
|
+
specify "#with_sql_first should return first row" do
|
3335
|
+
@ds.with_sql_first('SELECT * FROM foo').should == {:id=>1}
|
3336
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3337
|
+
end
|
3338
|
+
|
3339
|
+
specify "#with_sql_first should return nil if no rows returned" do
|
3340
|
+
@db.fetch = []
|
3341
|
+
@ds.with_sql_first('SELECT * FROM foo').should == nil
|
3342
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3343
|
+
end
|
3344
|
+
|
3345
|
+
specify "#with_sql_single_value should return first value from first row" do
|
3346
|
+
@ds.with_sql_single_value('SELECT * FROM foo').should == 1
|
3347
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3348
|
+
end
|
3349
|
+
|
3350
|
+
specify "#with_sql_single_value should return nil if no rows returned" do
|
3351
|
+
@db.fetch = []
|
3352
|
+
@ds.with_sql_single_value('SELECT * FROM foo').should == nil
|
3353
|
+
@db.sqls.should == ["SELECT * FROM foo -- read_only"]
|
3307
3354
|
end
|
3308
3355
|
end
|
3309
3356
|
|
@@ -4314,7 +4361,7 @@ describe "Dataset emulating bitwise operator support" do
|
|
4314
4361
|
@ds = Sequel::Database.new.dataset
|
4315
4362
|
@ds.quote_identifiers = true
|
4316
4363
|
def @ds.complex_expression_sql_append(sql, op, args)
|
4317
|
-
sql
|
4364
|
+
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:bitand, a, b)}
|
4318
4365
|
end
|
4319
4366
|
end
|
4320
4367
|
|
@@ -4562,6 +4609,49 @@ describe "Dataset#paged_each" do
|
|
4562
4609
|
@ds.limit(nil, 2).paged_each(:rows_per_fetch=>3, &@proc)
|
4563
4610
|
@ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 2", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 5", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 8", "SELECT * FROM test ORDER BY x LIMIT 3 OFFSET 11"]
|
4564
4611
|
end
|
4612
|
+
|
4613
|
+
it "should support :strategy=>:filter" do
|
4614
|
+
@ds._fetch = @db.each_slice(5).to_a
|
4615
|
+
@ds.paged_each(:rows_per_fetch=>5, :strategy=>:filter, &@proc)
|
4616
|
+
@ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY x LIMIT 5", "SELECT * FROM test WHERE (x > 4) ORDER BY x LIMIT 5", "SELECT * FROM test WHERE (x > 9) ORDER BY x LIMIT 5"]
|
4617
|
+
@rows.should == @db
|
4618
|
+
|
4619
|
+
@rows = []
|
4620
|
+
db = @db.map{|h| h[:y] = h[:x] % 5; h[:z] = h[:x] % 9; h}.sort_by{|h| [h[:z], -h[:y], h[:x]]}
|
4621
|
+
@ds._fetch = db.each_slice(5).to_a
|
4622
|
+
@ds.order(Sequel.identifier(:z), Sequel.desc(Sequel.qualify(:test, :y)), Sequel.asc(:x)).paged_each(:rows_per_fetch=>5, :strategy=>:filter, &@proc)
|
4623
|
+
@ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY z, test.y DESC, x ASC LIMIT 5",
|
4624
|
+
"SELECT * FROM test WHERE ((z > 3) OR ((z = 3) AND (test.y < 3)) OR ((z = 3) AND (test.y = 3) AND (x > 3))) ORDER BY z, test.y DESC, x ASC LIMIT 5",
|
4625
|
+
"SELECT * FROM test WHERE ((z > 8) OR ((z = 8) AND (test.y < 3)) OR ((z = 8) AND (test.y = 3) AND (x > 8))) ORDER BY z, test.y DESC, x ASC LIMIT 5"]
|
4626
|
+
@rows.should == db
|
4627
|
+
end
|
4628
|
+
|
4629
|
+
it "should support :strategy=>:filter with :filter_values option" do
|
4630
|
+
db = @db.map{|h| h[:y] = h[:x] % 5; h[:z] = h[:x] % 9; h}.sort_by{|h| [h[:z], -h[:y], h[:x]]}
|
4631
|
+
@ds._fetch = db.each_slice(5).to_a
|
4632
|
+
@ds.order(Sequel.identifier(:z), Sequel.desc(Sequel.qualify(:test, :y) * 2), Sequel.asc(:x)).paged_each(:rows_per_fetch=>5, :strategy=>:filter, :filter_values=>proc{|row, expr| [row[expr[0].value], row[expr[1].args.first.column] * expr[1].args.last, row[expr[2]]]}, &@proc)
|
4633
|
+
@ds.db.sqls[1...-1].should == ["SELECT * FROM test ORDER BY z, (test.y * 2) DESC, x ASC LIMIT 5",
|
4634
|
+
"SELECT * FROM test WHERE ((z > 3) OR ((z = 3) AND ((test.y * 2) < 6)) OR ((z = 3) AND ((test.y * 2) = 6) AND (x > 3))) ORDER BY z, (test.y * 2) DESC, x ASC LIMIT 5",
|
4635
|
+
"SELECT * FROM test WHERE ((z > 8) OR ((z = 8) AND ((test.y * 2) < 6)) OR ((z = 8) AND ((test.y * 2) = 6) AND (x > 8))) ORDER BY z, (test.y * 2) DESC, x ASC LIMIT 5"]
|
4636
|
+
@rows.should == db
|
4637
|
+
end
|
4638
|
+
end
|
4639
|
+
|
4640
|
+
describe "Dataset#current_datetime" do
|
4641
|
+
after do
|
4642
|
+
Sequel.datetime_class = Time
|
4643
|
+
end
|
4644
|
+
|
4645
|
+
it "should return an instance of Sequel.datetime_class for the current datetime" do
|
4646
|
+
t = Sequel::Dataset.new(nil).current_datetime
|
4647
|
+
t.should be_a_kind_of(Time)
|
4648
|
+
(Time.now - t < 0.1).should == true
|
4649
|
+
|
4650
|
+
Sequel.datetime_class = DateTime
|
4651
|
+
t = Sequel::Dataset.new(nil).current_datetime
|
4652
|
+
t.should be_a_kind_of(DateTime)
|
4653
|
+
(DateTime.now - t < (0.1/86400)).should == true
|
4654
|
+
end
|
4565
4655
|
end
|
4566
4656
|
|
4567
4657
|
describe "Dataset#escape_like" do
|
@@ -4693,3 +4783,58 @@ describe "Dataset mutation methods" do
|
|
4693
4783
|
dsc.graph!(dsc, {:b=>:c}, :table_alias=>:foo).ungraphed!.opts[:graph].should be_nil
|
4694
4784
|
end
|
4695
4785
|
end
|
4786
|
+
|
4787
|
+
describe "Dataset emulated complex expression operators" do
|
4788
|
+
before do
|
4789
|
+
@ds = Sequel.mock[:test]
|
4790
|
+
def @ds.complex_expression_sql_append(sql, op, args)
|
4791
|
+
case op
|
4792
|
+
when :&, :|, :^, :%, :<<, :>>, :'B~'
|
4793
|
+
complex_expression_emulate_append(sql, op, args)
|
4794
|
+
else
|
4795
|
+
super
|
4796
|
+
end
|
4797
|
+
end
|
4798
|
+
@n = Sequel.expr(:x).sql_number
|
4799
|
+
end
|
4800
|
+
|
4801
|
+
it "should emulate &" do
|
4802
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:&, @n)).should == "x"
|
4803
|
+
@ds.literal(@n & 1).should == "BITAND(x, 1)"
|
4804
|
+
@ds.literal(@n & 1 & 2).should == "BITAND(BITAND(x, 1), 2)"
|
4805
|
+
end
|
4806
|
+
|
4807
|
+
it "should emulate |" do
|
4808
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:|, @n)).should == "x"
|
4809
|
+
@ds.literal(@n | 1).should == "BITOR(x, 1)"
|
4810
|
+
@ds.literal(@n | 1 | 2).should == "BITOR(BITOR(x, 1), 2)"
|
4811
|
+
end
|
4812
|
+
|
4813
|
+
it "should emulate ^" do
|
4814
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:^, @n)).should == "x"
|
4815
|
+
@ds.literal(@n ^ 1).should == "BITXOR(x, 1)"
|
4816
|
+
@ds.literal(@n ^ 1 ^ 2).should == "BITXOR(BITXOR(x, 1), 2)"
|
4817
|
+
end
|
4818
|
+
|
4819
|
+
it "should emulate %" do
|
4820
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:%, @n)).should == "x"
|
4821
|
+
@ds.literal(@n % 1).should == "MOD(x, 1)"
|
4822
|
+
@ds.literal(@n % 1 % 2).should == "MOD(MOD(x, 1), 2)"
|
4823
|
+
end
|
4824
|
+
|
4825
|
+
it "should emulate >>" do
|
4826
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:>>, @n)).should == "x"
|
4827
|
+
@ds.literal(@n >> 1).should == "(x / power(2, 1))"
|
4828
|
+
@ds.literal(@n >> 1 >> 2).should == "(x / power(2, 1) / power(2, 2))"
|
4829
|
+
end
|
4830
|
+
|
4831
|
+
it "should emulate <<" do
|
4832
|
+
@ds.literal(Sequel::SQL::NumericExpression.new(:<<, @n)).should == "x"
|
4833
|
+
@ds.literal(@n << 1).should == "(x * power(2, 1))"
|
4834
|
+
@ds.literal(@n << 1 << 2).should == "(x * power(2, 1) * power(2, 2))"
|
4835
|
+
end
|
4836
|
+
|
4837
|
+
it "should emulate B~" do
|
4838
|
+
@ds.literal(~@n).should == "((0 - x) - 1)"
|
4839
|
+
end
|
4840
|
+
end
|
@@ -714,7 +714,7 @@ describe "Sequel core extension replacements" do
|
|
714
714
|
end
|
715
715
|
|
716
716
|
it "Sequel.& should join all arguments given with AND" do
|
717
|
-
l(Sequel.&(:a), "
|
717
|
+
l(Sequel.&(:a), "a")
|
718
718
|
l(Sequel.&(:a, :b=>:c), "(a AND (b = c))")
|
719
719
|
l(Sequel.&(:a, {:b=>:c}, Sequel.lit('d')), "(a AND (b = c) AND d)")
|
720
720
|
end
|
@@ -724,7 +724,7 @@ describe "Sequel core extension replacements" do
|
|
724
724
|
end
|
725
725
|
|
726
726
|
it "Sequel.| should join all arguments given with OR" do
|
727
|
-
l(Sequel.|(:a), "
|
727
|
+
l(Sequel.|(:a), "a")
|
728
728
|
l(Sequel.|(:a, :b=>:c), "(a OR (b = c))")
|
729
729
|
l(Sequel.|(:a, {:b=>:c}, Sequel.lit('d')), "(a OR (b = c) OR d)")
|
730
730
|
end
|
@@ -808,7 +808,7 @@ describe "Sequel core extension replacements" do
|
|
808
808
|
|
809
809
|
it "Sequel.{+,-,*,/} should accept arguments and use the appropriate operator" do
|
810
810
|
%w'+ - * /'.each do |op|
|
811
|
-
l(Sequel.send(op, 1), '
|
811
|
+
l(Sequel.send(op, 1), '1')
|
812
812
|
l(Sequel.send(op, 1, 2), "(1 #{op} 2)")
|
813
813
|
l(Sequel.send(op, 1, 2, 3), "(1 #{op} 2 #{op} 3)")
|
814
814
|
end
|
@@ -104,11 +104,20 @@ describe Sequel::Dataset, "graphing" do
|
|
104
104
|
|
105
105
|
it "should accept a SQL::Identifier as the dataset" do
|
106
106
|
ds = @ds1.graph(Sequel.identifier(:lines), :x=>:id)
|
107
|
-
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines
|
107
|
+
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
108
108
|
ds = @ds1.graph(Sequel.identifier('lines'), :x=>:id)
|
109
109
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines AS lines ON (lines.x = points.id)'
|
110
110
|
end
|
111
111
|
|
112
|
+
it "should handle a SQL::Identifier with double underscores correctly" do
|
113
|
+
ds = @ds1.graph(Sequel.identifier(:lin__es), :x=>:id)
|
114
|
+
ds.sql.should == 'SELECT points.id, points.x, points.y, lin__es.id AS lin__es_id, lin__es.name, lin__es.x AS lin__es_x, lin__es.y AS lin__es_y, lin__es.lines_x FROM points LEFT OUTER JOIN lin__es ON (lin__es.x = points.id)'
|
115
|
+
ds = @ds1.from(Sequel.identifier(:poi__nts)).graph(Sequel.identifier(:lin__es), :x=>:id)
|
116
|
+
ds.sql.should == 'SELECT poi__nts.id, poi__nts.name, poi__nts.x, poi__nts.y, poi__nts.lines_x, lin__es.id AS lin__es_id, lin__es.name AS lin__es_name, lin__es.x AS lin__es_x, lin__es.y AS lin__es_y, lin__es.lines_x AS lin__es_lines_x FROM poi__nts LEFT OUTER JOIN lin__es ON (lin__es.x = poi__nts.id)'
|
117
|
+
ds = @ds1.from(Sequel.identifier(:poi__nts).qualify(:foo)).graph(Sequel.identifier(:lin__es).qualify(:bar), :x=>:id)
|
118
|
+
ds.sql.should == 'SELECT foo.poi__nts.id, foo.poi__nts.x, foo.poi__nts.y, foo.poi__nts.graph_id, lin__es.id AS lin__es_id, lin__es.name, lin__es.x AS lin__es_x, lin__es.y AS lin__es_y, lin__es.lines_x FROM foo.poi__nts LEFT OUTER JOIN bar.lin__es AS lin__es ON (lin__es.x = foo.poi__nts.id)'
|
119
|
+
end
|
120
|
+
|
112
121
|
it "should accept a SQL::QualifiedIdentifier as the dataset" do
|
113
122
|
ds = @ds1.graph(Sequel.qualify(:schema, :lines), :x=>:id)
|
114
123
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN schema.lines AS lines ON (lines.x = points.id)'
|
@@ -120,6 +129,13 @@ describe Sequel::Dataset, "graphing" do
|
|
120
129
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN schema.lines AS lines ON (lines.x = points.id)'
|
121
130
|
end
|
122
131
|
|
132
|
+
it "should handle a qualified identifier as the source" do
|
133
|
+
ds = @ds1.from(:schema__points).graph(:lines, :x=>:id)
|
134
|
+
ds.sql.should == 'SELECT schema.points.id, schema.points.x, schema.points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM schema.points LEFT OUTER JOIN lines ON (lines.x = schema.points.id)'
|
135
|
+
ds = @ds1.from(Sequel.qualify(:schema, :points)).graph(:lines, :x=>:id)
|
136
|
+
ds.sql.should == 'SELECT schema.points.id, schema.points.x, schema.points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM schema.points LEFT OUTER JOIN lines ON (lines.x = schema.points.id)'
|
137
|
+
end
|
138
|
+
|
123
139
|
it "should accept a SQL::AliasedExpression as the dataset" do
|
124
140
|
ds = @ds1.graph(Sequel.as(:lines, :foo), :x=>:id)
|
125
141
|
ds.sql.should == 'SELECT points.id, points.x, points.y, foo.id AS foo_id, foo.x AS foo_x, foo.y AS foo_y, foo.graph_id FROM points LEFT OUTER JOIN lines AS foo ON (foo.x = points.id)'
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
2
|
+
|
3
|
+
describe "Dataset::PlaceholderLiteralizer" do
|
4
|
+
before do
|
5
|
+
@c = Sequel::Dataset::PlaceholderLiteralizer
|
6
|
+
@db = Sequel.mock
|
7
|
+
@ds = @db[:items]
|
8
|
+
@h = {:id=>1}
|
9
|
+
@ds.db.fetch = @h
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "should handle calls with no placeholders" do
|
13
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>1)}
|
14
|
+
loader.first.should == @h
|
15
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)"]
|
16
|
+
end
|
17
|
+
|
18
|
+
specify "should handle calls with a single placeholder" do
|
19
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
20
|
+
loader.first(1).should == @h
|
21
|
+
loader.first(2).should == @h
|
22
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)", "SELECT * FROM items WHERE (a = 2)"]
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "should handle calls with multiple placeholders" do
|
26
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg).where(:b=>Sequel.+(pl.arg, 1)).where(pl.arg)}
|
27
|
+
loader.first(1, :c, :id=>1).should == @h
|
28
|
+
loader.first(2, :d, :id=>2).should == @h
|
29
|
+
@db.sqls.should == ["SELECT * FROM items WHERE ((a = 1) AND (b = (c + 1)) AND (id = 1))", "SELECT * FROM items WHERE ((a = 2) AND (b = (d + 1)) AND (id = 2))"]
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "should handle calls with a placeholders used as filter arguments" do
|
33
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(pl.arg)}
|
34
|
+
loader.first(:id=>1).should == @h
|
35
|
+
loader.first(proc{a(b)}).should == @h
|
36
|
+
loader.first("a = 1").should == @h
|
37
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (id = 1)", "SELECT * FROM items WHERE a(b)", "SELECT * FROM items WHERE (a = 1)"]
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "should handle calls with a placeholders used as right hand side of condition specifiers" do
|
41
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
42
|
+
loader.first(1).should == @h
|
43
|
+
loader.first([1, 2]).should == @h
|
44
|
+
loader.first(nil).should == @h
|
45
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)", "SELECT * FROM items WHERE (a IN (1, 2))", "SELECT * FROM items WHERE (a IS NULL)"]
|
46
|
+
end
|
47
|
+
|
48
|
+
specify "should handle calls with a placeholder used multiple times" do
|
49
|
+
loader = @c.loader(@ds){|pl, ds| a = pl.arg; ds.where(:a=>a).where(:b=>a)}
|
50
|
+
loader.first(1).should == @h
|
51
|
+
loader.first(2).should == @h
|
52
|
+
@db.sqls.should == ["SELECT * FROM items WHERE ((a = 1) AND (b = 1))", "SELECT * FROM items WHERE ((a = 2) AND (b = 2))"]
|
53
|
+
end
|
54
|
+
|
55
|
+
specify "should handle calls with a placeholder used multiple times in different capacities" do
|
56
|
+
loader = @c.loader(@ds){|pl, ds| a = pl.arg; ds.where(a).where(:b=>a)}
|
57
|
+
loader.first("a = 1").should == @h
|
58
|
+
loader.first(["a = ?", 2]).should == @h
|
59
|
+
@db.sqls.should == ["SELECT * FROM items WHERE ((a = 1) AND (b = 'a = 1'))", "SELECT * FROM items WHERE ((a = 2) AND (b IN ('a = ?', 2)))"]
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "should handle calls with manually specified argument positions" do
|
63
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg(1)).where(:b=>pl.arg(0))}
|
64
|
+
loader.first(1, 2).should == @h
|
65
|
+
loader.first(2, 1).should == @h
|
66
|
+
@db.sqls.should == ["SELECT * FROM items WHERE ((a = 2) AND (b = 1))", "SELECT * FROM items WHERE ((a = 1) AND (b = 2))"]
|
67
|
+
end
|
68
|
+
|
69
|
+
specify "should handle dataset with row procs" do
|
70
|
+
@ds.row_proc = proc{|r| {:foo=>r[:id]+1}}
|
71
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
72
|
+
loader.first(1).should == {:foo=>2}
|
73
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)"]
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should return all rows for #all" do
|
77
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
78
|
+
loader.all(1).should == [@h]
|
79
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)"]
|
80
|
+
end
|
81
|
+
|
82
|
+
specify "should iterate over block for #all" do
|
83
|
+
a = []
|
84
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
85
|
+
loader.all(1){|r| a << r}.should == [@h]
|
86
|
+
a.should == [@h]
|
87
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)"]
|
88
|
+
end
|
89
|
+
|
90
|
+
specify "should iterate over block for #each" do
|
91
|
+
a = []
|
92
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
93
|
+
loader.each(1){|r| a << r}
|
94
|
+
a.should == [@h]
|
95
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 1)"]
|
96
|
+
end
|
97
|
+
|
98
|
+
specify "should return first value for #get" do
|
99
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
100
|
+
loader.get(2).should == 1
|
101
|
+
@db.sqls.should == ["SELECT * FROM items WHERE (a = 2)"]
|
102
|
+
end
|
103
|
+
|
104
|
+
specify "should raise an error if called with an incorrect number of arguments" do
|
105
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg)}
|
106
|
+
proc{loader.first}.should raise_error(Sequel::Error)
|
107
|
+
proc{loader.first(1, 2)}.should raise_error(Sequel::Error)
|
108
|
+
end
|
109
|
+
|
110
|
+
specify "should raise an error if called with an incorrect number of arguments when manually providing argument positions" do
|
111
|
+
loader = @c.loader(@ds){|pl, ds| ds.where(:a=>pl.arg(1))}
|
112
|
+
proc{loader.first}.should raise_error(Sequel::Error)
|
113
|
+
proc{loader.first(1)}.should raise_error(Sequel::Error)
|
114
|
+
proc{loader.first(1, 2, 3)}.should raise_error(Sequel::Error)
|
115
|
+
end
|
116
|
+
|
117
|
+
specify "should raise an error if argument literalized into a different string than returned by query" do
|
118
|
+
o = Object.new
|
119
|
+
def o.wrap(v)
|
120
|
+
@v = v
|
121
|
+
self
|
122
|
+
end
|
123
|
+
def o.sql_literal(ds)
|
124
|
+
ds.literal(@v)
|
125
|
+
end
|
126
|
+
proc{@c.loader(@ds){|pl, ds| ds.where(o.wrap(pl.arg))}}.should raise_error(Sequel::Error)
|
127
|
+
end
|
128
|
+
end
|
data/spec/core/schema_spec.rb
CHANGED
@@ -701,6 +701,12 @@ describe "DB#create_table!" do
|
|
701
701
|
@db.create_table!(:cats){|*a|}
|
702
702
|
@db.sqls.should == ['DROP TABLE cats', 'CREATE TABLE cats ()']
|
703
703
|
end
|
704
|
+
|
705
|
+
specify "should use IF EXISTS if the database supports it" do
|
706
|
+
meta_def(@db, :supports_drop_table_if_exists?){true}
|
707
|
+
@db.create_table!(:cats){|*a|}
|
708
|
+
@db.sqls.should == ['DROP TABLE IF EXISTS cats', 'CREATE TABLE cats ()']
|
709
|
+
end
|
704
710
|
end
|
705
711
|
|
706
712
|
describe "DB#create_table?" do
|
@@ -775,6 +781,54 @@ describe "DB#create_join_table" do
|
|
775
781
|
end
|
776
782
|
end
|
777
783
|
|
784
|
+
describe "DB#create_join_table?" do
|
785
|
+
before do
|
786
|
+
@db = Sequel.mock
|
787
|
+
end
|
788
|
+
|
789
|
+
specify "should create the table if it does not already exist" do
|
790
|
+
meta_def(@db, :table_exists?){|a| false}
|
791
|
+
@db.create_join_table?(:cat_id=>:cats, :dog_id=>:dogs)
|
792
|
+
@db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
793
|
+
end
|
794
|
+
|
795
|
+
specify "should not create the table if it already exists" do
|
796
|
+
meta_def(@db, :table_exists?){|a| true}
|
797
|
+
@db.create_join_table?(:cat_id=>:cats, :dog_id=>:dogs)
|
798
|
+
@db.sqls.should == []
|
799
|
+
end
|
800
|
+
|
801
|
+
specify "should use IF NOT EXISTS if the database supports it" do
|
802
|
+
meta_def(@db, :supports_create_table_if_not_exists?){true}
|
803
|
+
@db.create_join_table?(:cat_id=>:cats, :dog_id=>:dogs)
|
804
|
+
@db.sqls.should == ['CREATE TABLE IF NOT EXISTS cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
805
|
+
end
|
806
|
+
end
|
807
|
+
|
808
|
+
describe "DB#create_join_table!" do
|
809
|
+
before do
|
810
|
+
@db = Sequel.mock
|
811
|
+
end
|
812
|
+
|
813
|
+
specify "should drop the table first if it already exists" do
|
814
|
+
meta_def(@db, :table_exists?){|a| true}
|
815
|
+
@db.create_join_table!(:cat_id=>:cats, :dog_id=>:dogs)
|
816
|
+
@db.sqls.should == ['DROP TABLE cats_dogs', 'CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
817
|
+
end
|
818
|
+
|
819
|
+
specify "should not drop the table if it doesn't exists" do
|
820
|
+
meta_def(@db, :table_exists?){|a| false}
|
821
|
+
@db.create_join_table!(:cat_id=>:cats, :dog_id=>:dogs)
|
822
|
+
@db.sqls.should == ['CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
823
|
+
end
|
824
|
+
|
825
|
+
specify "should use IF EXISTS if the database supports it" do
|
826
|
+
meta_def(@db, :supports_drop_table_if_exists?){true}
|
827
|
+
@db.create_join_table!(:cat_id=>:cats, :dog_id=>:dogs)
|
828
|
+
@db.sqls.should == ['DROP TABLE IF EXISTS cats_dogs', 'CREATE TABLE cats_dogs (cat_id integer NOT NULL REFERENCES cats, dog_id integer NOT NULL REFERENCES dogs, PRIMARY KEY (cat_id, dog_id))', 'CREATE INDEX cats_dogs_dog_id_cat_id_index ON cats_dogs (dog_id, cat_id)']
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
778
832
|
describe "DB#drop_join_table" do
|
779
833
|
before do
|
780
834
|
@db = Sequel.mock
|