sequel 5.9.0 → 5.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.freeze.with_extend(m1, m2){def a; 4**super end}
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.freeze.with_row_proc(l)
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).freeze
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].freeze
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).freeze
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).freeze
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).freeze
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).freeze
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).freeze
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).freeze
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).freeze
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.freeze
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.freeze
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.freeze
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].freeze
5238
+ @ds = Sequel.mock[:test]
5229
5239
  end
5230
5240
 
5231
- it "should be returned by Dataset#freeze" do
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].freeze
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].freeze
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.freeze
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>"
@@ -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) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1))))"
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) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))) OR (tags.id IS NULL))"
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) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8)))))"
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) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))) OR (tags.id 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 _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 (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1)))"
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) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1))))))"
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 ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) OR (tags.id IS NULL))"
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) FROM artists 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))"
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 ((tags.id * 3) IN (SELECT unnest(artists.tag_ids[1:2]) FROM artists WHERE (id = 1)))"
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) FROM items WHERE (items.foo_ids IS NOT NULL)"
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