sequel 5.9.0 → 5.10.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.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/doc/release_notes/5.10.0.txt +84 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +20 -4
- data/lib/sequel/adapters/shared/postgres.rb +12 -2
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
- data/lib/sequel/database/schema_generator.rb +3 -0
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset/actions.rb +7 -6
- data/lib/sequel/dataset/misc.rb +14 -0
- data/lib/sequel/extensions/pg_array.rb +83 -79
- data/lib/sequel/extensions/pg_extended_date_support.rb +11 -4
- data/lib/sequel/extensions/pg_range.rb +4 -2
- data/lib/sequel/model/associations.rb +10 -2
- data/lib/sequel/plugins/list.rb +18 -8
- data/lib/sequel/plugins/pg_array_associations.rb +2 -2
- data/lib/sequel/plugins/tree.rb +28 -13
- data/lib/sequel/sql.rb +24 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +94 -26
- data/spec/bin_spec.rb +2 -0
- data/spec/core/dataset_spec.rb +58 -32
- data/spec/core/expression_filters_spec.rb +16 -0
- data/spec/core/spec_helper.rb +1 -0
- data/spec/core_extensions_spec.rb +1 -0
- data/spec/extensions/list_spec.rb +16 -0
- data/spec/extensions/pg_array_associations_spec.rb +10 -10
- data/spec/extensions/pg_range_spec.rb +34 -2
- data/spec/extensions/spec_helper.rb +1 -0
- data/spec/extensions/tree_spec.rb +40 -0
- data/spec/guards_helper.rb +1 -0
- data/spec/model/associations_spec.rb +21 -0
- data/spec/model/spec_helper.rb +1 -0
- metadata +4 -2
data/spec/bin_spec.rb
CHANGED
@@ -23,6 +23,8 @@ File.delete(BIN_SPEC_DB2) if File.file?(BIN_SPEC_DB2)
|
|
23
23
|
DB = Sequel.connect("#{CONN_PREFIX}#{BIN_SPEC_DB}", :test=>false)
|
24
24
|
DB2 = Sequel.connect("#{CONN_PREFIX}#{BIN_SPEC_DB2}", :test=>false)
|
25
25
|
|
26
|
+
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
27
|
+
gem 'minitest'
|
26
28
|
require 'minitest/autorun'
|
27
29
|
|
28
30
|
describe "bin/sequel" do
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -1729,7 +1729,7 @@ describe "Dataset#with_extend" do
|
|
1729
1729
|
m2 = Module.new{def a; 3 end}
|
1730
1730
|
d.with_extend(m1, m2){def a; 4**super end}.a.must_equal 65536
|
1731
1731
|
d.respond_to?(:a).must_equal false
|
1732
|
-
ds = d.
|
1732
|
+
ds = d.with_extend(m1, m2){def a; 4**super end}
|
1733
1733
|
ds.a.must_equal 65536
|
1734
1734
|
ds.frozen?.must_equal true
|
1735
1735
|
end
|
@@ -1865,7 +1865,7 @@ describe "Dataset#with_row_proc" do
|
|
1865
1865
|
l = lambda{|r| r}
|
1866
1866
|
d.with_row_proc(l).row_proc.must_equal l
|
1867
1867
|
d.row_proc.must_be_nil
|
1868
|
-
ds = d.
|
1868
|
+
ds = d.with_row_proc(l)
|
1869
1869
|
ds.frozen?.must_equal true
|
1870
1870
|
ds.row_proc.must_equal l
|
1871
1871
|
end
|
@@ -2094,7 +2094,7 @@ end
|
|
2094
2094
|
describe "Dataset#count" do
|
2095
2095
|
before do
|
2096
2096
|
@db = Sequel.mock(:fetch=>{:count=>1})
|
2097
|
-
@dataset = @db.from(:test).columns(:count)
|
2097
|
+
@dataset = @db.from(:test).columns(:count)
|
2098
2098
|
end
|
2099
2099
|
|
2100
2100
|
it "should format SQL properly" do
|
@@ -2656,7 +2656,7 @@ end
|
|
2656
2656
|
|
2657
2657
|
describe "Dataset aggregate methods" do
|
2658
2658
|
before do
|
2659
|
-
@d = Sequel.mock(:fetch=>proc{|s| {1=>s}})[:test]
|
2659
|
+
@d = Sequel.mock(:fetch=>proc{|s| {1=>s}})[:test]
|
2660
2660
|
end
|
2661
2661
|
|
2662
2662
|
it "should include min" do
|
@@ -2715,15 +2715,25 @@ describe "Dataset #first and #last" do
|
|
2715
2715
|
end
|
2716
2716
|
|
2717
2717
|
it "should return a single record if no argument is given" do
|
2718
|
-
ds = @d.order(:a)
|
2718
|
+
ds = @d.order(:a)
|
2719
2719
|
3.times do
|
2720
2720
|
ds.first.must_equal(:s=>'SELECT * FROM test ORDER BY a LIMIT 1')
|
2721
2721
|
ds.last.must_equal(:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1')
|
2722
2722
|
end
|
2723
2723
|
end
|
2724
2724
|
|
2725
|
+
it "should handle empty arrays and hashes" do
|
2726
|
+
ds = @d.order(:a)
|
2727
|
+
3.times do
|
2728
|
+
ds.first({}).must_equal(:s=>'SELECT * FROM test ORDER BY a LIMIT 1')
|
2729
|
+
ds.last({}).must_equal(:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1')
|
2730
|
+
ds.first([]).must_equal(:s=>'SELECT * FROM test ORDER BY a LIMIT 1')
|
2731
|
+
ds.last([]).must_equal(:s=>'SELECT * FROM test ORDER BY a DESC LIMIT 1')
|
2732
|
+
end
|
2733
|
+
end
|
2734
|
+
|
2725
2735
|
it "should return the first/last matching record if argument is not an Integer" do
|
2726
|
-
ds = @d.order(:a)
|
2736
|
+
ds = @d.order(:a)
|
2727
2737
|
5.times do
|
2728
2738
|
ds.first(:z => 26).must_equal(:s=>'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1')
|
2729
2739
|
ds.first([[:z, 15]]).must_equal(:s=>'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1')
|
@@ -2733,7 +2743,7 @@ describe "Dataset #first and #last" do
|
|
2733
2743
|
end
|
2734
2744
|
|
2735
2745
|
it "should set the limit and return an array of records if the given number is > 1" do
|
2736
|
-
ds = @d.order(:a)
|
2746
|
+
ds = @d.order(:a)
|
2737
2747
|
5.times do
|
2738
2748
|
i = rand(10) + 10
|
2739
2749
|
ds.first(i).must_equal [{:s=>"SELECT * FROM test ORDER BY a LIMIT #{i}"}]
|
@@ -2742,7 +2752,7 @@ describe "Dataset #first and #last" do
|
|
2742
2752
|
end
|
2743
2753
|
|
2744
2754
|
it "should return the first matching record if a block is given without an argument" do
|
2745
|
-
ds = @d.order(:name)
|
2755
|
+
ds = @d.order(:name)
|
2746
2756
|
5.times do
|
2747
2757
|
@d.first{z > 26}.must_equal(:s=>'SELECT * FROM test WHERE (z > 26) LIMIT 1')
|
2748
2758
|
ds.last{z > 26}.must_equal(:s=>'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1')
|
@@ -2750,7 +2760,7 @@ describe "Dataset #first and #last" do
|
|
2750
2760
|
end
|
2751
2761
|
|
2752
2762
|
it "should combine block and standard argument filters if argument is not an Integer" do
|
2753
|
-
ds = @d.order(:name)
|
2763
|
+
ds = @d.order(:name)
|
2754
2764
|
5.times do
|
2755
2765
|
@d.first(:y=>25){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
|
2756
2766
|
ds.last(:y=>16){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
|
@@ -2758,7 +2768,7 @@ describe "Dataset #first and #last" do
|
|
2758
2768
|
end
|
2759
2769
|
|
2760
2770
|
it "should combine block and standard argument filters if argument is a literal string" do
|
2761
|
-
ds = @d.order(:name)
|
2771
|
+
ds = @d.order(:name)
|
2762
2772
|
5.times do
|
2763
2773
|
@d.first(Sequel.lit('y = 25')){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 25) AND (z > 26)) LIMIT 1')
|
2764
2774
|
ds.last(Sequel.lit('y = 16')){z > 26}.must_equal(:s=>'SELECT * FROM test WHERE ((y = 16) AND (z > 26)) ORDER BY name DESC LIMIT 1')
|
@@ -2768,7 +2778,7 @@ describe "Dataset #first and #last" do
|
|
2768
2778
|
end
|
2769
2779
|
|
2770
2780
|
it "should filter and return an array of records if an Integer argument is provided and a block is given" do
|
2771
|
-
ds = @d.order(:a)
|
2781
|
+
ds = @d.order(:a)
|
2772
2782
|
5.times do
|
2773
2783
|
i = rand(10) + 10
|
2774
2784
|
ds.first(i){z > 26}.must_equal [{:s=>"SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"}]
|
@@ -3038,7 +3048,7 @@ describe "Dataset#get" do
|
|
3038
3048
|
end
|
3039
3049
|
|
3040
3050
|
it "should select the specified column and fetch its value" do
|
3041
|
-
@d
|
3051
|
+
@d
|
3042
3052
|
5.times do
|
3043
3053
|
@d.get(:name).must_equal "SELECT name FROM test LIMIT 1"
|
3044
3054
|
@d.get(:abc).must_equal "SELECT abc FROM test LIMIT 1"
|
@@ -3050,14 +3060,14 @@ describe "Dataset#get" do
|
|
3050
3060
|
end
|
3051
3061
|
|
3052
3062
|
it "should work with aliased fields" do
|
3053
|
-
@d
|
3063
|
+
@d
|
3054
3064
|
5.times do
|
3055
3065
|
@d.get(Sequel.expr(Sequel[:x][:b]).as(:name)).must_equal "SELECT x.b AS name FROM test LIMIT 1"
|
3056
3066
|
end
|
3057
3067
|
end
|
3058
3068
|
|
3059
3069
|
it "should work with plain strings" do
|
3060
|
-
@d
|
3070
|
+
@d
|
3061
3071
|
5.times do
|
3062
3072
|
@d.get('a').must_equal "SELECT 'a' AS v FROM test LIMIT 1"
|
3063
3073
|
end
|
@@ -5225,33 +5235,20 @@ end
|
|
5225
5235
|
|
5226
5236
|
describe "Frozen Datasets" do
|
5227
5237
|
before do
|
5228
|
-
@ds = Sequel.mock[:test]
|
5238
|
+
@ds = Sequel.mock[:test]
|
5229
5239
|
end
|
5230
5240
|
|
5231
|
-
it "should be
|
5241
|
+
it "datasets should be frozen by default" do
|
5232
5242
|
@ds.must_be :frozen?
|
5233
5243
|
end
|
5234
5244
|
|
5235
5245
|
it "should have Dataset#freeze return receiver" do
|
5236
|
-
@ds = Sequel.mock[:test]
|
5237
|
-
@ds.freeze.must_be_same_as(@ds)
|
5238
|
-
end
|
5239
|
-
|
5240
|
-
it "should have Dataset#freeze be a no-op" do
|
5241
5246
|
@ds.freeze.must_be_same_as(@ds)
|
5242
5247
|
end
|
5243
5248
|
|
5244
5249
|
it "should have clones be frozen" do
|
5245
5250
|
@ds.clone.must_be :frozen?
|
5246
5251
|
end
|
5247
|
-
|
5248
|
-
it "should be equal to unfrozen ones" do
|
5249
|
-
@ds.must_equal @ds.db[:test]
|
5250
|
-
end
|
5251
|
-
|
5252
|
-
it "should not raise an error when calling query methods" do
|
5253
|
-
@ds.select(:a).sql.must_equal 'SELECT a FROM test'
|
5254
|
-
end
|
5255
5252
|
end
|
5256
5253
|
|
5257
5254
|
describe "Dataset emulated complex expression operators" do
|
@@ -5379,7 +5376,7 @@ end
|
|
5379
5376
|
|
5380
5377
|
describe "Dataset#where_all" do
|
5381
5378
|
before do
|
5382
|
-
@ds = Sequel.mock(:fetch=>{:id=>1})[:items]
|
5379
|
+
@ds = Sequel.mock(:fetch=>{:id=>1})[:items]
|
5383
5380
|
end
|
5384
5381
|
|
5385
5382
|
it "should filter dataset with condition, and return related rows" do
|
@@ -5389,6 +5386,15 @@ describe "Dataset#where_all" do
|
|
5389
5386
|
end
|
5390
5387
|
end
|
5391
5388
|
|
5389
|
+
it "should handle empty arrays and hashes" do
|
5390
|
+
5.times do
|
5391
|
+
@ds.where_all([]).must_equal [{:id=>1}]
|
5392
|
+
@ds.db.sqls.must_equal ['SELECT * FROM items']
|
5393
|
+
@ds.where_all({}).must_equal [{:id=>1}]
|
5394
|
+
@ds.db.sqls.must_equal ['SELECT * FROM items']
|
5395
|
+
end
|
5396
|
+
end
|
5397
|
+
|
5392
5398
|
it "should yield each row to the given block" do
|
5393
5399
|
5.times do
|
5394
5400
|
a = []
|
@@ -5401,7 +5407,18 @@ end
|
|
5401
5407
|
|
5402
5408
|
describe "Dataset#where_each" do
|
5403
5409
|
before do
|
5404
|
-
@ds = Sequel.mock(:fetch=>{:id=>1})[:items]
|
5410
|
+
@ds = Sequel.mock(:fetch=>{:id=>1})[:items]
|
5411
|
+
end
|
5412
|
+
|
5413
|
+
it "should handle empty arrays and hashes" do
|
5414
|
+
[[], {}].each do |arg|
|
5415
|
+
5.times do
|
5416
|
+
a = []
|
5417
|
+
@ds.where_each(arg){|r| a << r}
|
5418
|
+
a.must_equal [{:id=>1}]
|
5419
|
+
@ds.db.sqls.must_equal ['SELECT * FROM items']
|
5420
|
+
end
|
5421
|
+
end
|
5405
5422
|
end
|
5406
5423
|
|
5407
5424
|
it "should yield each row to the given block" do
|
@@ -5418,7 +5435,16 @@ describe "Dataset#where_single_value" do
|
|
5418
5435
|
before do
|
5419
5436
|
@ds = Sequel.mock(:fetch=>{:id=>1})[:items].with_extend do
|
5420
5437
|
select :only_id, :id
|
5421
|
-
end
|
5438
|
+
end
|
5439
|
+
end
|
5440
|
+
|
5441
|
+
it "should handle empty arrays and hashes" do
|
5442
|
+
[[], {}].each do |arg|
|
5443
|
+
5.times do
|
5444
|
+
@ds.only_id.where_single_value(arg).must_equal 1
|
5445
|
+
@ds.db.sqls.must_equal ['SELECT id FROM items LIMIT 1']
|
5446
|
+
end
|
5447
|
+
end
|
5422
5448
|
end
|
5423
5449
|
|
5424
5450
|
it "should return single value" do
|
@@ -503,6 +503,12 @@ describe "Blockless Ruby Filters" do
|
|
503
503
|
end
|
504
504
|
dsc.new(@d.db).literal(Sequel.trim(:a)).must_equal 'trimFOO(lower(a))'
|
505
505
|
end
|
506
|
+
|
507
|
+
it "should endless ranges" do
|
508
|
+
endless = eval('1..')
|
509
|
+
@d.l{x =~ endless}.must_equal '(x >= 1)'
|
510
|
+
@d.l(:x => endless).must_equal '(x >= 1)'
|
511
|
+
end if RUBY_VERSION >= '2.6'
|
506
512
|
end
|
507
513
|
|
508
514
|
describe Sequel::SQL::VirtualRow do
|
@@ -1140,6 +1146,16 @@ describe "Sequel::SQLTime" do
|
|
1140
1146
|
Sequel::SQLTime.create(1, 2, 3).strftime('%Y-%m-%d').must_equal Date.new(2000).strftime('%Y-%m-%d')
|
1141
1147
|
end
|
1142
1148
|
|
1149
|
+
it ".parse should respect SQLTime.date setting" do
|
1150
|
+
Sequel::SQLTime.date = Date.new(2000, 2, 3)
|
1151
|
+
Sequel::SQLTime.parse('10:11:12').strftime('%F').must_equal "2000-02-03"
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
it ".parse should respect application_timezone setting" do
|
1155
|
+
Sequel::application_timezone = :utc
|
1156
|
+
Sequel::SQLTime.parse('10:11:12').utc_offset.must_equal 0
|
1157
|
+
end
|
1158
|
+
|
1143
1159
|
it "#inspect should show class and time by default" do
|
1144
1160
|
Sequel::SQLTime.create(1, 2, 3).inspect.must_equal "#<Sequel::SQLTime 01:02:03>"
|
1145
1161
|
Sequel::SQLTime.create(13, 24, 35).inspect.must_equal "#<Sequel::SQLTime 13:24:35>"
|
data/spec/core/spec_helper.rb
CHANGED
@@ -8,6 +8,7 @@ end
|
|
8
8
|
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
|
9
9
|
require_relative "../../lib/sequel/core"
|
10
10
|
|
11
|
+
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
11
12
|
gem 'minitest'
|
12
13
|
require 'minitest/autorun'
|
13
14
|
require 'minitest/hooks/default'
|
@@ -14,6 +14,7 @@ Sequel.extension :core_extensions
|
|
14
14
|
Sequel.extension :symbol_aref
|
15
15
|
Sequel.extension :virtual_row_method_block
|
16
16
|
|
17
|
+
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
17
18
|
gem 'minitest'
|
18
19
|
require 'minitest/autorun'
|
19
20
|
require 'minitest/hooks/default'
|
@@ -17,6 +17,8 @@ describe "List plugin" do
|
|
17
17
|
@o = @c.load(:id=>7, :position=>3)
|
18
18
|
@sc = klass(:scope=>:scope_id)
|
19
19
|
@so = @sc.load(:id=>7, :position=>3, :scope_id=>5)
|
20
|
+
@tc = klass(:top=>0)
|
21
|
+
@to = @tc.load(:id=>7, :position=>3)
|
20
22
|
@db.reset
|
21
23
|
end
|
22
24
|
|
@@ -43,6 +45,14 @@ describe "List plugin" do
|
|
43
45
|
klass(:scope=>proc{|o| o.model.dataset.filter(:active).filter(:scope_id=>o.scope_id)}).new(:scope_id=>4).list_dataset.sql.must_equal 'SELECT * FROM items WHERE (active AND (scope_id = 4)) ORDER BY position'
|
44
46
|
end
|
45
47
|
|
48
|
+
it "should default top of the list to 1" do
|
49
|
+
@c.top_of_list.must_equal 1
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should accept a :top option to set top of the list" do
|
53
|
+
@tc.top_of_list.must_equal 0
|
54
|
+
end
|
55
|
+
|
46
56
|
it "should modify the order when using the plugin" do
|
47
57
|
c = Class.new(Sequel::Model(:items))
|
48
58
|
c.dataset.sql.must_equal 'SELECT * FROM items'
|
@@ -199,6 +209,12 @@ describe "List plugin" do
|
|
199
209
|
"UPDATE items SET position = 1 WHERE (id = 7)"]
|
200
210
|
end
|
201
211
|
|
212
|
+
it "should have move_to_top use position 0 when :top_of_list is 0" do
|
213
|
+
@to.move_to_top
|
214
|
+
@db.sqls.must_equal ["UPDATE items SET position = (position + 1) WHERE ((position >= 0) AND (position < 3))",
|
215
|
+
"UPDATE items SET position = 0 WHERE (id = 7)"]
|
216
|
+
end
|
217
|
+
|
202
218
|
it "should have move_up without an argument move up a single position" do
|
203
219
|
@o.move_up.must_equal @o
|
204
220
|
@o.position.must_equal 2
|
@@ -72,7 +72,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
72
72
|
|
73
73
|
it "should allowing filtering by associations with :conditions" do
|
74
74
|
@c1.filter(:a_tags=>@o2).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), false)"
|
75
|
-
@c2.filter(:a_artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids)
|
75
|
+
@c2.filter(:a_artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1))))"
|
76
76
|
end
|
77
77
|
|
78
78
|
it "should allowing excluding by associations" do
|
@@ -82,7 +82,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
82
82
|
|
83
83
|
it "should allowing excluding by associations with :conditions" do
|
84
84
|
@c1.exclude(:a_tags=>@o2).sql.must_equal "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), false) OR (artists.tag_ids IS NULL))"
|
85
|
-
@c2.exclude(:a_artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids)
|
85
|
+
@c2.exclude(:a_artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))) OR (tags.id IS NULL))"
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should allowing filtering by multiple associations" do
|
@@ -92,7 +92,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
92
92
|
|
93
93
|
it "should allowing filtering by multiple associations with :conditions" do
|
94
94
|
@c1.filter(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), false)"
|
95
|
-
@c2.filter(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids)
|
95
|
+
@c2.filter(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8)))))"
|
96
96
|
end
|
97
97
|
|
98
98
|
it "should allowing excluding by multiple associations" do
|
@@ -102,7 +102,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
102
102
|
|
103
103
|
it "should allowing excluding by multiple associations with :conditions" do
|
104
104
|
@c1.exclude(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.must_equal "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), false) OR (artists.tag_ids IS NULL))"
|
105
|
-
@c2.exclude(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids)
|
105
|
+
@c2.exclude(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))) OR (tags.id IS NULL))"
|
106
106
|
end
|
107
107
|
|
108
108
|
it "should allowing filtering/excluding associations with NULL or empty values" do
|
@@ -120,22 +120,22 @@ describe Sequel::Model, "pg_array_associations" do
|
|
120
120
|
|
121
121
|
it "should allowing filtering by association datasets" do
|
122
122
|
@c1.filter(:tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), false)"
|
123
|
-
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (
|
123
|
+
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (EXISTS (SELECT 1 FROM (SELECT artists.tag_ids AS key FROM artists WHERE (id = 1)) AS t1 WHERE (tags.id = any(key))))"
|
124
124
|
end
|
125
125
|
|
126
126
|
it "should allowing filtering by association datasets with :conditions" do
|
127
127
|
@c1.filter(:a_tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), false)"
|
128
|
-
@c2.filter(:a_artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids)
|
128
|
+
@c2.filter(:a_artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (tags.id IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1))))))"
|
129
129
|
end
|
130
130
|
|
131
131
|
it "should allowing excluding by association datasets" do
|
132
132
|
@c1.exclude(:tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), false) OR (artists.tag_ids IS NULL))"
|
133
|
-
@c2.exclude(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE ((
|
133
|
+
@c2.exclude(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (NOT (EXISTS (SELECT 1 FROM (SELECT artists.tag_ids AS key FROM artists WHERE (id = 1)) AS t1 WHERE (tags.id = any(key)))) OR (tags.id IS NULL))"
|
134
134
|
end
|
135
135
|
|
136
136
|
it "should allowing excluding by association datasets with :conditions" do
|
137
137
|
@c1.exclude(:a_tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), false) OR (artists.tag_ids IS NULL))"
|
138
|
-
@c2.exclude(:a_artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids)
|
138
|
+
@c2.exclude(:a_artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT _smtopgaa_key_ FROM artists CROSS JOIN unnest(artists.tag_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))) OR (tags.id IS NULL))"
|
139
139
|
end
|
140
140
|
|
141
141
|
it "filter by associations should respect key options" do
|
@@ -146,7 +146,7 @@ describe Sequel::Model, "pg_array_associations" do
|
|
146
146
|
@c1.filter(:tags=>@o2).sql.must_equal "SELECT * FROM artists WHERE (artists.tag_ids[1:2] @> ARRAY[6]::integer[])"
|
147
147
|
@c2.filter(:artists=>@o1).sql.must_equal "SELECT * FROM tags WHERE ((tags.id * 3) IN (3, 6, 9))"
|
148
148
|
@c1.filter(:tags=>@c2.where(:id=>1)).sql.must_equal "SELECT * FROM artists WHERE coalesce((artists.tag_ids[1:2] && (SELECT array_agg((tags.id * 3)) FROM tags WHERE (id = 1))), false)"
|
149
|
-
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE ((
|
149
|
+
@c2.filter(:artists=>@c1.where(:id=>1)).sql.must_equal "SELECT * FROM tags WHERE (EXISTS (SELECT 1 FROM (SELECT artists.tag_ids[1:2] AS key FROM artists WHERE (id = 1)) AS t1 WHERE ((tags.id * 3) = any(key))))"
|
150
150
|
end
|
151
151
|
|
152
152
|
it "should raise an error if associated model does not have a primary key, and :primary_key is not specified" do
|
@@ -793,7 +793,7 @@ describe "Sequel::Model.finalize_associations" do
|
|
793
793
|
r[:_dataset].sql.must_equal "SELECT * FROM items"
|
794
794
|
r[:associated_eager_dataset].sql.must_equal "SELECT * FROM items"
|
795
795
|
r.fetch(:_eager_limit_strategy).must_be_nil
|
796
|
-
r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT unnest(items.foo_ids)
|
796
|
+
r[:filter_by_associations_conditions_dataset].sql.must_equal "SELECT _smtopgaa_key_ FROM items CROSS JOIN unnest(items.foo_ids) AS _smtopgaa_(_smtopgaa_key_) WHERE (items.foo_ids IS NOT NULL)"
|
797
797
|
r[:predicate_key].must_equal Sequel.qualify(:items, :foo_ids)
|
798
798
|
r[:predicate_keys].must_equal [Sequel.qualify(:items, :foo_ids)]
|
799
799
|
r[:reciprocal].must_equal :foos
|
@@ -16,6 +16,8 @@ describe "pg_range extension" do
|
|
16
16
|
@db.extension(:pg_array, :pg_range)
|
17
17
|
end
|
18
18
|
|
19
|
+
endless_range_support = RUBY_VERSION >= '2.6'
|
20
|
+
|
19
21
|
it "should set up conversion procs correctly" do
|
20
22
|
cp = @db.conversion_procs
|
21
23
|
cp[3904].call("[1,2]").must_equal @R.new(1,2, :exclude_begin=>false, :exclude_end=>false, :db_type=>'int4range')
|
@@ -48,6 +50,10 @@ describe "pg_range extension" do
|
|
48
50
|
@db.literal(''..'()[]",\\2').must_equal "'[\"\",\\(\\)\\[\\]\\\"\\,\\\\2]'"
|
49
51
|
end
|
50
52
|
|
53
|
+
it "should literalize endless Range instances to strings correctly" do
|
54
|
+
@db.literal(eval('1..')).must_equal "'[1,]'"
|
55
|
+
end if endless_range_support
|
56
|
+
|
51
57
|
it "should literalize PGRange instances to strings correctly" do
|
52
58
|
@db.literal(@R.new(1, 2)).must_equal "'[1,2]'"
|
53
59
|
@db.literal(@R.new(true, false)).must_equal "'[true,false]'"
|
@@ -71,6 +77,10 @@ describe "pg_range extension" do
|
|
71
77
|
@db.bound_variable_arg(1..2, nil).must_equal "[1,2]"
|
72
78
|
end
|
73
79
|
|
80
|
+
it "should support using endless Range instances as bound variables" do
|
81
|
+
@db.bound_variable_arg(eval('1..'), nil).must_equal "[1,]"
|
82
|
+
end if endless_range_support
|
83
|
+
|
74
84
|
it "should support using PGRange instances as bound variables" do
|
75
85
|
@db.bound_variable_arg(@R.new(1, 2), nil).must_equal "[1,2]"
|
76
86
|
end
|
@@ -79,6 +89,10 @@ describe "pg_range extension" do
|
|
79
89
|
@db.bound_variable_arg([1..2,2...3], nil).must_equal '{"[1,2]","[2,3)"}'
|
80
90
|
end
|
81
91
|
|
92
|
+
it "should support using arrays of endless Range instances as bound variables" do
|
93
|
+
@db.bound_variable_arg([eval('1..'), eval('2..')], nil).must_equal '{"[1,]","[2,]"}'
|
94
|
+
end if endless_range_support
|
95
|
+
|
82
96
|
it "should support using PGRange instances as bound variables" do
|
83
97
|
@db.bound_variable_arg([@R.new(1, 2),@R.new(2, 3)], nil).must_equal '{"[1,2]","[2,3]"}'
|
84
98
|
end
|
@@ -415,6 +429,14 @@ describe "pg_range extension" do
|
|
415
429
|
@R.new(nil, nil).wont_be :==, (1..2)
|
416
430
|
end
|
417
431
|
|
432
|
+
it "should consider PGRanges equal with a endless Range they represent" do
|
433
|
+
@R.new(1, nil).must_be :==, eval('1..')
|
434
|
+
end if endless_range_support
|
435
|
+
|
436
|
+
it "should not consider a PGRange equal with a Range if it can't be expressed as a range" do
|
437
|
+
@R.new(1, nil).wont_be :==, eval('2..')
|
438
|
+
end if endless_range_support
|
439
|
+
|
418
440
|
it "should not consider a PGRange equal to other objects" do
|
419
441
|
@R.new(nil, nil).wont_equal 1
|
420
442
|
end
|
@@ -450,7 +472,6 @@ describe "pg_range extension" do
|
|
450
472
|
|
451
473
|
it "should have #to_range raise an exception if the PGRange cannot be represented by a Range" do
|
452
474
|
proc{@R.new(nil, 1).to_range}.must_raise(Sequel::Error)
|
453
|
-
proc{@R.new(1, nil).to_range}.must_raise(Sequel::Error)
|
454
475
|
proc{@R.new(0, 1, :exclude_begin=>true).to_range}.must_raise(Sequel::Error)
|
455
476
|
proc{@R.empty.to_range}.must_raise(Sequel::Error)
|
456
477
|
end
|
@@ -459,6 +480,14 @@ describe "pg_range extension" do
|
|
459
480
|
@r1.to_range.must_be :==, (1..2)
|
460
481
|
end
|
461
482
|
|
483
|
+
it "should have #to_range return the represented range for endless ranges" do
|
484
|
+
@R.new(1, nil).to_range.must_be :==, eval('1..')
|
485
|
+
end if endless_range_support
|
486
|
+
|
487
|
+
it "should have #to_range raise an exception for endless ranges" do
|
488
|
+
proc{@R.new(1, nil).to_range}.must_raise(Sequel::Error)
|
489
|
+
end unless endless_range_support
|
490
|
+
|
462
491
|
it "should have #to_range cache the returned value" do
|
463
492
|
@r1.to_range.must_be_same_as(@r1.to_range)
|
464
493
|
end
|
@@ -479,9 +508,12 @@ describe "pg_range extension" do
|
|
479
508
|
|
480
509
|
it "should have #valid_ruby_range? return false if the PGRange cannot be represented as a Range" do
|
481
510
|
@R.new(nil, 1).valid_ruby_range?.must_equal false
|
482
|
-
@R.new(1, nil).valid_ruby_range?.must_equal false
|
483
511
|
@R.new(0, 1, :exclude_begin=>true).valid_ruby_range?.must_equal false
|
484
512
|
@R.empty.valid_ruby_range?.must_equal false
|
485
513
|
end
|
514
|
+
|
515
|
+
it "should have #valid_ruby_range return #{endless_range_support} for endless ranges" do
|
516
|
+
@R.new(1, nil).valid_ruby_range?.must_equal(endless_range_support)
|
517
|
+
end
|
486
518
|
end
|
487
519
|
end
|