sequel 5.26.0 → 5.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c7e9baac1a3a326ffdc2506dde798441b376fecbe71594cd91efb54bc56a9a7
4
- data.tar.gz: d051640fc5fb89053beaa43915eca9920222d7978be91bc704cde94f1b425532
3
+ metadata.gz: 512f72ad99402b33be776e759611a9299b638f49344fb1f349120c8cd1643107
4
+ data.tar.gz: ea0852b3ea61ef9a5a192637a2b457fe47f2374c8ee2eb20d9b02e6eb25a25ac
5
5
  SHA512:
6
- metadata.gz: 0e424392185dec97acca2092fb5b3127a6619033dd62eef683f29501ece0ecacd17ada2a2b1edf27682155c6d59633fa88a475dd1ebf4bdbf8f1641f73a566a8
7
- data.tar.gz: 8c1ac728b94de7b5f0cf9a3df230f35732906accea64a0b61b6eba90203e49396eacc95786ad7a78bc56b3b812cda8bd94d4e8f1d0be188b9d9ae4f49792a9de
6
+ metadata.gz: 4397a4a7530bd3234d7c1e3bf8b1c2b8a6845e659b86b7177dc704edf8ebf2185c499ac7ba04bb7d52006cf1367b8c0835787be1483e7bb481fa4ea2d775680b
7
+ data.tar.gz: 653238c46ae32bd74127bddb87dc4c99637f5eadfb7515a51e8da5bc57df10de57c2e9a97d085828cc45b97303fa66580c1c8832a5f6fbb5cdaffd585ce0ff86
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ === 5.27.0 (2019-12-01)
2
+
3
+ * Add Sequel::DEFAULT for a DEFAULT expression, useful for assigning to default values (jeremyevans)
4
+
5
+ * Make Postgres::ArrayOp#join in pg_array_ops extension work correctly on PostgreSQL <9.1 (jeremyevans)
6
+
7
+ * Make pg_enum extension work correctly on PostgreSQL 8.3-9.0 (jeremyevans)
8
+
9
+ * Emulate FILTER clause for aggregate functions using CASE on databases not supporting it directly (jeremyevans)
10
+
11
+ * Support ordering by NULLS FIRST/NULLS LAST without emulation on SQLite 3.30+ (jeremyevans)
12
+
1
13
  === 5.26.0 (2019-11-01)
2
14
 
3
15
  * Recognize two additional foreign key constraint violation codes on MySQL 8.0.13+ (rianmcguire) (#1657)
@@ -0,0 +1,21 @@
1
+ = New Features
2
+
3
+ * Sequel::DEFAULT has been added a constant for the DEFAULT expression,
4
+ useful in inserts and especially updates:
5
+
6
+ DB[:a].where(:id=>1).update(:b=>Sequel::DEFAULT)
7
+ # UPDATE "a" SET "b" = DEFAULT WHERE "id" = 1
8
+
9
+ * SQL::Function#filter for filtered aggregate functions is now
10
+ supported on all databases. On databases not supporting it natively
11
+ (all except PostgreSQL 9.4+ and SQLite 3.30+), a CASE statement is
12
+ used to emulate the support.
13
+
14
+ = Other Improvements
15
+
16
+ * NULLS FIRST/LAST is now used without emulation on SQLite 3.30+.
17
+
18
+ * The pg_enum extension now works correctly on PostgreSQL 8.3-9.0.
19
+
20
+ * Postgres::ArrayOp#join in the pg_array_ops extension now works
21
+ correctly on PostgreSQL <9.1.
@@ -1955,6 +1955,11 @@ module Sequel
1955
1955
  db.server_version(@opts[:server])
1956
1956
  end
1957
1957
 
1958
+ # PostgreSQL 9.4+ supports the FILTER clause for aggregate functions.
1959
+ def supports_filtered_aggregates?
1960
+ server_version >= 90400
1961
+ end
1962
+
1958
1963
  # PostgreSQL supports quoted function names.
1959
1964
  def supports_quoted_function_names?
1960
1965
  true
@@ -869,9 +869,9 @@ module Sequel
869
869
  end
870
870
  end
871
871
 
872
- # SQLite does not natively support NULLS FIRST/LAST.
872
+ # SQLite supports NULLS FIRST/LAST natively in 3.30+.
873
873
  def requires_emulating_nulls_first?
874
- true
874
+ db.sqlite_version < 33000
875
875
  end
876
876
 
877
877
  # SQLite does not support FOR UPDATE, but silently ignore it
@@ -897,6 +897,11 @@ module Sequel
897
897
  false
898
898
  end
899
899
 
900
+ # SQLite 3.30 supports the FILTER clause for aggregate functions.
901
+ def supports_filtered_aggregates?
902
+ db.sqlite_version >= 33000
903
+ end
904
+
900
905
  # SQLite supports quoted function names.
901
906
  def supports_quoted_function_names?
902
907
  true
@@ -230,6 +230,12 @@ module Sequel
230
230
  supports_cte_in_subqueries?
231
231
  end
232
232
 
233
+ # Whether the dataset supports the FILTER clause for aggregate functions.
234
+ # If not, support is emulated using CASE.
235
+ def supports_filtered_aggregates?
236
+ false
237
+ end
238
+
233
239
  # Whether the database supports quoting function names.
234
240
  def supports_quoted_function_names?
235
241
  false
@@ -492,11 +492,24 @@ module Sequel
492
492
  end
493
493
 
494
494
  sql << '('
495
+ if filter = opts[:filter]
496
+ filter = filter_expr(filter, &opts[:filter_block])
497
+ end
495
498
  if opts[:*]
496
- sql << '*'
499
+ if filter && !supports_filtered_aggregates?
500
+ literal_append(sql, Sequel.case({filter=>1}, nil))
501
+ filter = nil
502
+ else
503
+ sql << '*'
504
+ end
497
505
  else
498
506
  sql << "DISTINCT " if opts[:distinct]
499
- expression_list_append(sql, f.args)
507
+ if filter && !supports_filtered_aggregates?
508
+ expression_list_append(sql, f.args.map{|arg| Sequel.case({filter=>arg}, nil)})
509
+ filter = nil
510
+ else
511
+ expression_list_append(sql, f.args)
512
+ end
500
513
  if order = opts[:order]
501
514
  sql << " ORDER BY "
502
515
  expression_list_append(sql, order)
@@ -510,9 +523,9 @@ module Sequel
510
523
  sql << ')'
511
524
  end
512
525
 
513
- if filter = opts[:filter]
526
+ if filter
514
527
  sql << " FILTER (WHERE "
515
- literal_append(sql, filter_expr(filter, &opts[:filter_block]))
528
+ literal_append(sql, filter)
516
529
  sql << ')'
517
530
  end
518
531
 
@@ -51,8 +51,8 @@
51
51
  # ia.length(2) # array_length(int_array_column, 2)
52
52
  # ia.lower # array_lower(int_array_column, 1)
53
53
  # ia.lower(2) # array_lower(int_array_column, 2)
54
- # ia.join # array_to_string(int_array_column, '', NULL)
55
- # ia.join(':') # array_to_string(int_array_column, ':', NULL)
54
+ # ia.join # array_to_string(int_array_column, '')
55
+ # ia.join(':') # array_to_string(int_array_column, ':')
56
56
  # ia.join(':', ' ') # array_to_string(int_array_column, ':', ' ')
57
57
  # ia.unnest # unnest(int_array_column)
58
58
  # ia.unnest(:b) # unnest(int_array_column, b)
@@ -217,12 +217,16 @@ module Sequel
217
217
 
218
218
  # Call the array_to_string method:
219
219
  #
220
- # array_op.join # array_to_string(array, '', NULL)
221
- # array_op.to_string # array_to_string(array, '', NULL)
222
- # array_op.join(":") # array_to_string(array, ':', NULL)
220
+ # array_op.join # array_to_string(array, '')
221
+ # array_op.to_string # array_to_string(array, '')
222
+ # array_op.join(":") # array_to_string(array, ':')
223
223
  # array_op.join(":", "*") # array_to_string(array, ':', '*')
224
224
  def to_string(joiner="", null=nil)
225
- function(:array_to_string, joiner, null)
225
+ if null.nil?
226
+ function(:array_to_string, joiner)
227
+ else
228
+ function(:array_to_string, joiner, null)
229
+ end
226
230
  end
227
231
  alias join to_string
228
232
 
@@ -133,8 +133,11 @@ module Sequel
133
133
  # the pg_type table to get names and array oids for
134
134
  # enums.
135
135
  def parse_enum_labels
136
+ order = [:enumtypid]
137
+ order << :enumsortorder if server_version >= 90100
138
+
136
139
  enum_labels = metadata_dataset.from(:pg_enum).
137
- order(:enumtypid, :enumsortorder).
140
+ order(*order).
138
141
  select_hash_groups(Sequel.cast(:enumtypid, Integer).as(:v), :enumlabel).freeze
139
142
  enum_labels.each_value(&:freeze)
140
143
 
@@ -1317,6 +1317,7 @@ module Sequel
1317
1317
  CURRENT_DATE = Constant.new(:CURRENT_DATE)
1318
1318
  CURRENT_TIME = Constant.new(:CURRENT_TIME)
1319
1319
  CURRENT_TIMESTAMP = Constant.new(:CURRENT_TIMESTAMP)
1320
+ DEFAULT = Constant.new(:DEFAULT)
1320
1321
  SQLTRUE = TRUE = BooleanConstant.new(true)
1321
1322
  SQLFALSE = FALSE = BooleanConstant.new(false)
1322
1323
  NULL = BooleanConstant.new(nil)
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 26
9
+ MINOR = 27
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
@@ -70,7 +70,7 @@ describe "PostgreSQL", '#create_table' do
70
70
 
71
71
  it "should create an unlogged table" do
72
72
  @db.create_table(:unlogged_dolls, :unlogged => true){text :name}
73
- end
73
+ end if DB.server_version >= 90100
74
74
 
75
75
  it "should create a table inheriting from another table" do
76
76
  @db.create_table(:unlogged_dolls){text :name}
@@ -615,10 +615,6 @@ describe "A PostgreSQL dataset" do
615
615
  @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode.function.within_group(:a)}.must_equal 1
616
616
  end if DB.server_version >= 90400
617
617
 
618
- it "should support filtered aggregate functions" do
619
- @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{count(:a).filter(:a=>1)}.must_equal 2
620
- end if DB.server_version >= 90400
621
-
622
618
  it "should support functions with ordinality" do
623
619
  @d.from{generate_series(1,10,3).with_ordinality}.select_map([:generate_series, :ordinality]).must_equal [[1, 1], [4, 2], [7, 3], [10, 4]]
624
620
  end if DB.server_version >= 90400
@@ -1272,7 +1268,7 @@ describe "A PostgreSQL database" do
1272
1268
  end
1273
1269
 
1274
1270
  it "should support indexes with index type" do
1275
- @db.create_table(:posts){point :p; index :p, :type => 'gist'}
1271
+ @db.create_table(:posts){box :geom; index :geom, :type => 'gist'}
1276
1272
  end
1277
1273
 
1278
1274
  it "should support unique indexes with index type" do
@@ -2704,6 +2700,8 @@ describe 'PostgreSQL array handling' do
2704
2700
  if @db.server_version >= 90000
2705
2701
  @ds.get(Sequel.pg_array(:i5).join).must_equal '15'
2706
2702
  @ds.get(Sequel.pg_array(:i5).join(':')).must_equal '1:5'
2703
+ end
2704
+ if @db.server_version >= 90100
2707
2705
  @ds.get(Sequel.pg_array(:i5).join(':', '*')).must_equal '1:*:5'
2708
2706
  end
2709
2707
  if @db.server_version >= 90300
@@ -4475,4 +4473,4 @@ describe "pg_auto_constraint_validations plugin" do
4475
4473
  File.delete(cache_file) if File.file?(cache_file)
4476
4474
  end
4477
4475
  end
4478
- end if DB.respond_to?(:error_info)
4476
+ end if DB.respond_to?(:error_info) && DB.server_version >= 90300
@@ -623,7 +623,7 @@ describe "SQLite", 'INSERT ON CONFLICT' do
623
623
  @ds.insert_conflict(:target=>:a).insert(1, 3, 4, false)
624
624
  @ds.insert_conflict(:target=>:c, :conflict_where=>:c_is_unique).insert(11, 12, 3, true)
625
625
  @ds.all.must_equal [{:a=>1, :b=>2, :c=>3, :c_is_unique=>false}, {:a=>10, :b=>11, :c=>3, :c_is_unique=>true}]
626
- end
626
+ end unless DB.adapter_scheme == :amalgalite
627
627
 
628
628
  it "Dataset#insert_ignore and insert_conflict should work with multi_insert/import" do
629
629
  @ds.insert(1, 2, 3, false)
@@ -353,6 +353,11 @@ describe "Blockless Ruby Filters" do
353
353
  @d.l({:x => Sequel::FALSE}).must_equal '(x IS FALSE)'
354
354
  @d.l({:x => Sequel::SQLTRUE}).must_equal '(x IS TRUE)'
355
355
  @d.l({:x => Sequel::SQLFALSE}).must_equal '(x IS FALSE)'
356
+
357
+ @d.l({:x => Sequel::CURRENT_DATE}).must_equal '(x = CURRENT_DATE)'
358
+ @d.l({:x => Sequel::CURRENT_TIME}).must_equal '(x = CURRENT_TIME)'
359
+ @d.l({:x => Sequel::CURRENT_TIMESTAMP}).must_equal '(x = CURRENT_TIMESTAMP)'
360
+ @d.l({:x => Sequel::DEFAULT}).must_equal '(x = DEFAULT)'
356
361
  end
357
362
 
358
363
  it "should support negation of SQL::Constants" do
@@ -741,14 +746,23 @@ describe Sequel::SQL::VirtualRow do
741
746
  @d.l{mode.function.within_group(:a, :b)}.must_equal 'mode() WITHIN GROUP (ORDER BY "a", "b")'
742
747
  end
743
748
 
749
+ it "should handle emualted filtered aggregate function calls" do
750
+ @d.l{count.function.*.filter(Sequel.&(:a, :b))}.must_equal 'count((CASE WHEN ("a" AND "b") THEN 1 ELSE NULL END))'
751
+ @d.l{count.function.*.filter(:a=>1)}.must_equal 'count((CASE WHEN ("a" = 1) THEN 1 ELSE NULL END))'
752
+ @d.l{count(:a).filter{b > 1}}.must_equal 'count((CASE WHEN ("b" > 1) THEN "a" ELSE NULL END))'
753
+ @d.l{count(:a).filter(:a=>1){b > 1}}.must_equal 'count((CASE WHEN (("a" = 1) AND ("b" > 1)) THEN "a" ELSE NULL END))'
754
+ end
755
+
744
756
  it "should handle filtered aggregate function calls" do
757
+ @d = @d.with_extend{def supports_filtered_aggregates?; true end}
745
758
  @d.l{count.function.*.filter(Sequel.&(:a, :b))}.must_equal 'count(*) FILTER (WHERE ("a" AND "b"))'
746
759
  @d.l{count.function.*.filter(:a=>1)}.must_equal 'count(*) FILTER (WHERE ("a" = 1))'
747
760
  @d.l{count.function.*.filter{b > 1}}.must_equal 'count(*) FILTER (WHERE ("b" > 1))'
748
761
  @d.l{count.function.*.filter(:a=>1){b > 1}}.must_equal 'count(*) FILTER (WHERE (("a" = 1) AND ("b" > 1)))'
749
762
  end
750
763
 
751
- it "should handle fitlered ordered-set and hypothetical-set function calls" do
764
+ it "should handle filtered ordered-set and hypothetical-set function calls" do
765
+ @d = @d.with_extend{def supports_filtered_aggregates?; true end}
752
766
  @d.l{mode.function.within_group(:a).filter(:a=>1)}.must_equal 'mode() WITHIN GROUP (ORDER BY "a") FILTER (WHERE ("a" = 1))'
753
767
  end
754
768
 
@@ -83,9 +83,9 @@ describe "Sequel::Postgres::ArrayOp" do
83
83
  end
84
84
 
85
85
  it "#to_string/join should use the array_to_string function" do
86
- @db.literal(@a.to_string).must_equal "array_to_string(a, '', NULL)"
87
- @db.literal(@a.join).must_equal "array_to_string(a, '', NULL)"
88
- @db.literal(@a.join(':')).must_equal "array_to_string(a, ':', NULL)"
86
+ @db.literal(@a.to_string).must_equal "array_to_string(a, '')"
87
+ @db.literal(@a.join).must_equal "array_to_string(a, '')"
88
+ @db.literal(@a.join(':')).must_equal "array_to_string(a, ':')"
89
89
  @db.literal(@a.join(':', '*')).must_equal "array_to_string(a, ':', '*')"
90
90
  end
91
91
 
@@ -92,6 +92,17 @@ describe "Simple Dataset operations" do
92
92
  @ds.from_self(:alias=>:items).graph(@ds.from_self, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
93
93
  end
94
94
 
95
+ cspecify "should have insert and update work with Sequel::DEFAULT", :sqlite do
96
+ @db.create_table!(:items) do
97
+ Integer :number, :default=>10
98
+ end
99
+ @ds.insert(:number=>Sequel::DEFAULT)
100
+ @ds.select_map(:number).must_equal [10]
101
+ @ds.insert(:number=>20)
102
+ @ds.update(:number=>Sequel::DEFAULT)
103
+ @ds.select_map(:number).must_equal [10, 10]
104
+ end
105
+
95
106
  cspecify "should have insert work correctly when inserting a row with all NULL values", :hsqldb do
96
107
  @db.create_table!(:items) do
97
108
  String :name
@@ -443,6 +454,20 @@ describe Sequel::Dataset do
443
454
  @d.extension(:sequel_4_dataset_methods).interval(:value).to_i.must_equal 6
444
455
  end
445
456
 
457
+ it "should support or emulate filtered aggregate functions" do
458
+ @d.insert(:name => 'abc', :value => 123)
459
+ @d.insert(:name => 'abc', :value => 456)
460
+ @d.insert(:name => 'def', :value => 324)
461
+ @d.get{count.function.*.filter{value > 100}}.must_equal 3
462
+ @d.get{count.function.*.filter{value > 200}}.must_equal 2
463
+ @d.get{count.function.*.filter{value > 400}}.must_equal 1
464
+ @d.get{count.function.*.filter{value > 500}}.must_equal 0
465
+ @d.get{count(:value).filter{value > 100}}.must_equal 3
466
+ @d.get{count(:value).filter{value > 200}}.must_equal 2
467
+ @d.get{count(:value).filter{value > 400}}.must_equal 1
468
+ @d.get{count(:value).filter{value > 500}}.must_equal 0
469
+ end
470
+
446
471
  it "should return the correct records" do
447
472
  @d.to_a.must_equal []
448
473
  @d.insert(:name => 'abc', :value => 123)
@@ -2393,7 +2393,7 @@ describe "string_agg extension" do
2393
2393
  cspecify "should have string_agg return aggregated concatenation for distinct values", :mssql, :sqlite, :oracle, :db2, :derby do
2394
2394
  @ds.select_group(:id).select_append(Sequel.string_agg(:s).order(:s).distinct.as(:v)).map([:id, :v]).must_equal [[1, 'a,b,c'], [2, 'aa,bb']]
2395
2395
  end
2396
- end
2396
+ end if (DB.database_type != :postgres || DB.server_version >= 90000)
2397
2397
 
2398
2398
  describe "insert_conflict plugin" do
2399
2399
  before(:all) do
@@ -62,4 +62,10 @@ if ENV['SEQUEL_FREEZE_DATABASE']
62
62
  DB.freeze
63
63
  end
64
64
 
65
- puts "running #{defined?(SEQUEL_ADAPTER_TEST) ? SEQUEL_ADAPTER_TEST : "integration (database type: #{DB.database_type})"} specs on #{RUBY_ENGINE} #{defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION} with #{DB.adapter_scheme} adapter"
65
+ version = if DB.respond_to?(:server_version)
66
+ DB.server_version
67
+ elsif DB.respond_to?(:sqlite_version)
68
+ DB.sqlite_version
69
+ end
70
+
71
+ puts "running #{defined?(SEQUEL_ADAPTER_TEST) ? SEQUEL_ADAPTER_TEST : "integration (database type: #{DB.database_type})"} specs on #{RUBY_ENGINE} #{defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION} with #{DB.adapter_scheme} adapter#{" (database version: #{version})" if version}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.26.0
4
+ version: 5.27.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-01 00:00:00.000000000 Z
11
+ date: 2019-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -224,6 +224,7 @@ extra_rdoc_files:
224
224
  - doc/release_notes/5.24.0.txt
225
225
  - doc/release_notes/5.25.0.txt
226
226
  - doc/release_notes/5.26.0.txt
227
+ - doc/release_notes/5.27.0.txt
227
228
  files:
228
229
  - CHANGELOG
229
230
  - MIT-LICENSE
@@ -321,6 +322,7 @@ files:
321
322
  - doc/release_notes/5.24.0.txt
322
323
  - doc/release_notes/5.25.0.txt
323
324
  - doc/release_notes/5.26.0.txt
325
+ - doc/release_notes/5.27.0.txt
324
326
  - doc/release_notes/5.3.0.txt
325
327
  - doc/release_notes/5.4.0.txt
326
328
  - doc/release_notes/5.5.0.txt