sequel 5.25.0 → 5.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +14 -0
- data/README.rdoc +1 -1
- data/doc/postgresql.rdoc +2 -2
- data/doc/release_notes/5.26.0.txt +35 -0
- data/lib/sequel/adapters/shared/postgres.rb +10 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/extensions/pg_json.rb +1 -1
- data/lib/sequel/extensions/pg_json_ops.rb +124 -0
- data/lib/sequel/extensions/pg_range.rb +9 -0
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/model/base.rb +12 -5
- data/lib/sequel/plugins/caching.rb +3 -0
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +3 -9
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/typecast_on_load.rb +3 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +67 -0
- data/spec/extensions/dirty_spec.rb +33 -0
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_json_ops_spec.rb +67 -0
- data/spec/extensions/pg_range_spec.rb +14 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c7e9baac1a3a326ffdc2506dde798441b376fecbe71594cd91efb54bc56a9a7
|
4
|
+
data.tar.gz: d051640fc5fb89053beaa43915eca9920222d7978be91bc704cde94f1b425532
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e424392185dec97acca2092fb5b3127a6619033dd62eef683f29501ece0ecacd17ada2a2b1edf27682155c6d59633fa88a475dd1ebf4bdbf8f1641f73a566a8
|
7
|
+
data.tar.gz: 8c1ac728b94de7b5f0cf9a3df230f35732906accea64a0b61b6eba90203e49396eacc95786ad7a78bc56b3b812cda8bd94d4e8f1d0be188b9d9ae4f49792a9de
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
=== 5.26.0 (2019-11-01)
|
2
|
+
|
3
|
+
* Recognize two additional foreign key constraint violation codes on MySQL 8.0.13+ (rianmcguire) (#1657)
|
4
|
+
|
5
|
+
* Support table aliases for single-table INSERT statements on PostgreSQL 9.5+ (jeremyevans) (#1656)
|
6
|
+
|
7
|
+
* Implement Sequel::Postgres::PGRange#hash so instances work correctly in hashes (jeremyevans) (#1648)
|
8
|
+
|
9
|
+
* Make dirty plugin work correctly with typecast_on_load plugin (jeremyevans) (#1647)
|
10
|
+
|
11
|
+
* Add support for :require_modification option when setting up nested_attributes (jeremyevans)
|
12
|
+
|
13
|
+
* Add support for SQL/JSON path expressions to the pg_json_ops extension, supported by PostgreSQL 12+ (jeremyevans)
|
14
|
+
|
1
15
|
=== 5.25.0 (2019-10-01)
|
2
16
|
|
3
17
|
* Fix Sequel::SQL::NumericMethods#coerce to not raise NoMethodError if super method is not defined (jeremyevans) (#1645)
|
data/README.rdoc
CHANGED
@@ -578,7 +578,7 @@ A single model instance can also be fetched by specifying a condition:
|
|
578
578
|
post = Post.first(title: 'hello world')
|
579
579
|
post = Post.first{num_comments < 10}
|
580
580
|
|
581
|
-
The dataset for a model class returns rows
|
581
|
+
The dataset for a model class returns rows of model instances instead of plain hashes:
|
582
582
|
|
583
583
|
DB[:posts].first.class # => Hash
|
584
584
|
Post.first.class # => Post
|
data/doc/postgresql.rdoc
CHANGED
@@ -341,12 +341,12 @@ Dataset#overriding_system_value and Dataset#overriding_user_value to use this ne
|
|
341
341
|
syntax:
|
342
342
|
|
343
343
|
DB.create_table(:table){primary_key :id}
|
344
|
-
# Ignore the given value for id, using the identity's sequence value
|
344
|
+
# Ignore the given value for id, using the identity's sequence value.
|
345
345
|
DB[:table].overriding_user_value.insert(:id=>1)
|
346
346
|
|
347
347
|
DB.create_table(:table){primary_key :id, :identity=>:always}
|
348
348
|
# Force the use of the given value for id, because otherwise the insert will
|
349
|
-
# raise an error, since GENERATED ALWAYS was
|
349
|
+
# raise an error, since GENERATED ALWAYS was used when creating the column.
|
350
350
|
DB[:table].overriding_system_value.insert(:id=>1)
|
351
351
|
|
352
352
|
=== Distinct On Specific Columns
|
@@ -0,0 +1,35 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Support for SQL/JSON path expressions has been added to the
|
4
|
+
pg_json_ops extension. These are supported in PostgreSQL 12+.
|
5
|
+
Examples:
|
6
|
+
|
7
|
+
j = Sequel.pg_json_op(:json_column)
|
8
|
+
j.path_exists('$.foo') # (jsonb_column @? '$.foo')
|
9
|
+
j.path_match('$.foo') # (jsonb_column @@ '$.foo')
|
10
|
+
j.path_exists!('$.foo') # jsonb_path_exists(jsonb_column, '$.foo')
|
11
|
+
j.path_match!('$.foo') # jsonb_path_match(jsonb_column, '$.foo')
|
12
|
+
j.path_query('$.foo') # jsonb_path_query(jsonb_column, '$.foo')
|
13
|
+
j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
14
|
+
j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
15
|
+
|
16
|
+
* The nested_attributes method in the nested_attributes plugin now
|
17
|
+
supports a :require_modification option, which can override the
|
18
|
+
default require_modification setting for the nested objects. This
|
19
|
+
can be useful to avoid errors if multiple requests are submitted
|
20
|
+
simultaneously to delete the same nested row.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* The dirty plugin now works correctly with the typecast_on_load
|
25
|
+
plugin.
|
26
|
+
|
27
|
+
* Sequel::Postgres::PGRange#hash has been added to the pg_range
|
28
|
+
extension, allowing PGRange instances to be usable as hash keys.
|
29
|
+
|
30
|
+
* Table aliases are now supported for single table INSERT
|
31
|
+
statements on PostgreSQL 9.5+, which can make some insert_conflict
|
32
|
+
usage easier.
|
33
|
+
|
34
|
+
* Two more foreign key constraint violation types are now recognized
|
35
|
+
on MySQL 8.0.13+.
|
@@ -1806,6 +1806,16 @@ module Sequel
|
|
1806
1806
|
end
|
1807
1807
|
end
|
1808
1808
|
|
1809
|
+
# Include aliases when inserting into a single table on PostgreSQL 9.5+.
|
1810
|
+
def insert_into_sql(sql)
|
1811
|
+
sql << " INTO "
|
1812
|
+
if (f = @opts[:from]) && f.length == 1
|
1813
|
+
identifier_append(sql, server_version >= 90500 ? f.first : unaliased_identifier(f.first))
|
1814
|
+
else
|
1815
|
+
source_list_append(sql, f)
|
1816
|
+
end
|
1817
|
+
end
|
1818
|
+
|
1809
1819
|
# Return the primary key to use for RETURNING in an INSERT statement
|
1810
1820
|
def insert_pk
|
1811
1821
|
if (f = opts[:from]) && !f.empty?
|
@@ -12,7 +12,7 @@
|
|
12
12
|
#
|
13
13
|
# Note that wrapping JSON primitives changes the behavior for
|
14
14
|
# JSON false and null values. Because only +false+ and +nil+
|
15
|
-
# in Ruby are considered
|
15
|
+
# in Ruby are considered falsey, wrapping these objects results
|
16
16
|
# in unexpected behavior if you use the values directly in
|
17
17
|
# conditionals:
|
18
18
|
#
|
@@ -73,6 +73,23 @@
|
|
73
73
|
# j.pretty # jsonb_pretty(jsonb_column)
|
74
74
|
# j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
|
75
75
|
#
|
76
|
+
# On PostgreSQL 12+ SQL/JSON functions and operators are supported:
|
77
|
+
#
|
78
|
+
# j.path_exists('$.foo') # (jsonb_column @? '$.foo')
|
79
|
+
# j.path_match('$.foo') # (jsonb_column @@ '$.foo')
|
80
|
+
#
|
81
|
+
# j.path_exists!('$.foo') # jsonb_path_exists(jsonb_column, '$.foo')
|
82
|
+
# j.path_match!('$.foo') # jsonb_path_match(jsonb_column, '$.foo')
|
83
|
+
# j.path_query('$.foo') # jsonb_path_query(jsonb_column, '$.foo')
|
84
|
+
# j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
85
|
+
# j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
86
|
+
#
|
87
|
+
# For the PostgreSQL 12+ SQL/JSON functions, one argument is required (+path+) and
|
88
|
+
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
89
|
+
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
90
|
+
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
91
|
+
# errors are not suppressed.
|
92
|
+
#
|
76
93
|
# If you are also using the pg_json extension, you should load it before
|
77
94
|
# loading this extension. Doing so will allow you to use the #op method on
|
78
95
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -292,6 +309,8 @@ module Sequel
|
|
292
309
|
CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
|
293
310
|
DELETE_PATH = ["(".freeze, " #- ".freeze, ")".freeze].freeze
|
294
311
|
HAS_KEY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
|
312
|
+
PATH_EXISTS = ["(".freeze, " @? ".freeze, ")".freeze].freeze
|
313
|
+
PATH_MATCH = ["(".freeze, " @@ ".freeze, ")".freeze].freeze
|
295
314
|
|
296
315
|
# jsonb expression for deletion of the given argument from the
|
297
316
|
# current jsonb.
|
@@ -362,6 +381,95 @@ module Sequel
|
|
362
381
|
self.class.new(function(:insert, wrap_input_array(path), wrap_input_jsonb(other), insert_after))
|
363
382
|
end
|
364
383
|
|
384
|
+
# Returns whether the JSON path returns any item for the json object.
|
385
|
+
#
|
386
|
+
# json_op.path_exists("$.foo") # (json @? '$.foo')
|
387
|
+
def path_exists(path)
|
388
|
+
bool_op(PATH_EXISTS, path)
|
389
|
+
end
|
390
|
+
|
391
|
+
# Returns whether the JSON path returns any item for the json object.
|
392
|
+
#
|
393
|
+
# json_op.path_exists!("$.foo")
|
394
|
+
# # jsonb_path_exists(json, '$.foo')
|
395
|
+
#
|
396
|
+
# json_op.path_exists!("$.foo ? ($ > $x)", x: 2)
|
397
|
+
# # jsonb_path_exists(json, '$.foo ? ($ > $x)', '{"x":2}')
|
398
|
+
#
|
399
|
+
# json_op.path_exists!("$.foo ? ($ > $x)", {x: 2}, true)
|
400
|
+
# # jsonb_path_exists(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
401
|
+
def path_exists!(path, vars=nil, silent=nil)
|
402
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists, path, vars, silent))
|
403
|
+
end
|
404
|
+
|
405
|
+
# Returns the first item of the result of JSON path predicate check for the json object.
|
406
|
+
# Returns nil if the first item is not true or false.
|
407
|
+
#
|
408
|
+
# json_op.path_match("$.foo") # (json @@ '$.foo')
|
409
|
+
def path_match(path)
|
410
|
+
bool_op(PATH_MATCH, path)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Returns the first item of the result of JSON path predicate check for the json object.
|
414
|
+
# Returns nil if the first item is not true or false and silent is true.
|
415
|
+
#
|
416
|
+
# json_op.path_match!("$.foo")
|
417
|
+
# # jsonb_path_match(json, '$.foo')
|
418
|
+
#
|
419
|
+
# json_op.path_match!("$.foo ? ($ > $x)", x: 2)
|
420
|
+
# # jsonb_path_match(json, '$.foo ? ($ > $x)', '{"x":2}')
|
421
|
+
#
|
422
|
+
# json_op.path_match!("$.foo ? ($ > $x)", {x: 2}, true)
|
423
|
+
# # jsonb_path_match(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
424
|
+
def path_match!(path, vars=nil, silent=nil)
|
425
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match, path, vars, silent))
|
426
|
+
end
|
427
|
+
|
428
|
+
# Returns a set of all jsonb values specified by the JSON path
|
429
|
+
# for the json object.
|
430
|
+
#
|
431
|
+
# json_op.path_query("$.foo")
|
432
|
+
# # jsonb_path_query(json, '$.foo')
|
433
|
+
#
|
434
|
+
# json_op.path_query("$.foo ? ($ > $x)", x: 2)
|
435
|
+
# # jsonb_path_query(json, '$.foo ? ($ > $x)', '{"x":2}')
|
436
|
+
#
|
437
|
+
# json_op.path_query("$.foo ? ($ > $x)", {x: 2}, true)
|
438
|
+
# # jsonb_path_query(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
439
|
+
def path_query(path, vars=nil, silent=nil)
|
440
|
+
_path_function(:jsonb_path_query, path, vars, silent)
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns a jsonb array of all values specified by the JSON path
|
444
|
+
# for the json object.
|
445
|
+
#
|
446
|
+
# json_op.path_query_array("$.foo")
|
447
|
+
# # jsonb_path_query_array(json, '$.foo')
|
448
|
+
#
|
449
|
+
# json_op.path_query_array("$.foo ? ($ > $x)", x: 2)
|
450
|
+
# # jsonb_path_query_array(json, '$.foo ? ($ > $x)', '{"x":2}')
|
451
|
+
#
|
452
|
+
# json_op.path_query_array("$.foo ? ($ > $x)", {x: 2}, true)
|
453
|
+
# # jsonb_path_query_array(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
454
|
+
def path_query_array(path, vars=nil, silent=nil)
|
455
|
+
JSONBOp.new(_path_function(:jsonb_path_query_array, path, vars, silent))
|
456
|
+
end
|
457
|
+
|
458
|
+
# Returns the first item of the result specified by the JSON path
|
459
|
+
# for the json object.
|
460
|
+
#
|
461
|
+
# json_op.path_query_first("$.foo")
|
462
|
+
# # jsonb_path_query_first(json, '$.foo')
|
463
|
+
#
|
464
|
+
# json_op.path_query_first("$.foo ? ($ > $x)", x: 2)
|
465
|
+
# # jsonb_path_query_first(json, '$.foo ? ($ > $x)', '{"x":2}')
|
466
|
+
#
|
467
|
+
# json_op.path_query_first("$.foo ? ($ > $x)", {x: 2}, true)
|
468
|
+
# # jsonb_path_query_first(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
469
|
+
def path_query_first(path, vars=nil, silent=nil)
|
470
|
+
JSONBOp.new(_path_function(:jsonb_path_query_first, path, vars, silent))
|
471
|
+
end
|
472
|
+
|
365
473
|
# Return the receiver, since it is already a JSONBOp.
|
366
474
|
def pg_jsonb
|
367
475
|
self
|
@@ -386,6 +494,22 @@ module Sequel
|
|
386
494
|
|
387
495
|
private
|
388
496
|
|
497
|
+
# Internals of the jsonb SQL/JSON path functions.
|
498
|
+
def _path_function(func, path, vars, silent)
|
499
|
+
args = []
|
500
|
+
if vars
|
501
|
+
if vars.is_a?(Hash)
|
502
|
+
vars = vars.to_json
|
503
|
+
end
|
504
|
+
args << vars
|
505
|
+
|
506
|
+
unless silent.nil?
|
507
|
+
args << silent
|
508
|
+
end
|
509
|
+
end
|
510
|
+
SQL::Function.new(func, self, path, *args)
|
511
|
+
end
|
512
|
+
|
389
513
|
# Return a placeholder literal with the given str and args, wrapped
|
390
514
|
# in a boolean expression, used by operators that return booleans.
|
391
515
|
def bool_op(str, other)
|
@@ -402,6 +402,15 @@ module Sequel
|
|
402
402
|
end
|
403
403
|
alias == eql?
|
404
404
|
|
405
|
+
# Make sure equal ranges have the same hash.
|
406
|
+
def hash
|
407
|
+
if @empty
|
408
|
+
@db_type.hash
|
409
|
+
else
|
410
|
+
[@begin, @end, @exclude_begin, @exclude_end, @db_type].hash
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
405
414
|
# Allow PGRange values in case statements, where they return true if they
|
406
415
|
# are equal to each other using eql?, or if this PGRange can be converted
|
407
416
|
# to a Range, delegating to that range.
|
@@ -9,8 +9,8 @@
|
|
9
9
|
# #
|
10
10
|
#
|
11
11
|
# As you can see, this uses single line SQL comments (--) suffixed
|
12
|
-
# by a newline. This
|
13
|
-
#
|
12
|
+
# by a newline. This plugin transforms all consecutive whitespace
|
13
|
+
# in the comment to a single string:
|
14
14
|
#
|
15
15
|
# ds = DB[:table].comment("Some\r\nComment Here").all
|
16
16
|
# # SELECT * FROM table -- Some Comment Here
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1069,7 +1069,7 @@ module Sequel
|
|
1069
1069
|
@new = true
|
1070
1070
|
@modified = true
|
1071
1071
|
initialize_set(values)
|
1072
|
-
|
1072
|
+
_clear_changed_columns(:initialize)
|
1073
1073
|
yield self if block_given?
|
1074
1074
|
end
|
1075
1075
|
|
@@ -1626,6 +1626,13 @@ module Sequel
|
|
1626
1626
|
def _changed_columns
|
1627
1627
|
@changed_columns ||= []
|
1628
1628
|
end
|
1629
|
+
|
1630
|
+
# Clear the changed columns. Reason is the reason for clearing
|
1631
|
+
# the columns, and should be one of: :initialize, :refresh, :create
|
1632
|
+
# or :update.
|
1633
|
+
def _clear_changed_columns(_reason)
|
1634
|
+
_changed_columns.clear
|
1635
|
+
end
|
1629
1636
|
|
1630
1637
|
# Do the deletion of the object's dataset, and check that the row
|
1631
1638
|
# was actually deleted.
|
@@ -1716,7 +1723,7 @@ module Sequel
|
|
1716
1723
|
# is used for reading newly inserted values from the database
|
1717
1724
|
def _refresh(dataset)
|
1718
1725
|
_refresh_set_values(_refresh_get(dataset) || raise(NoExistingObject, "Record not found"))
|
1719
|
-
|
1726
|
+
_clear_changed_columns(:refresh)
|
1720
1727
|
end
|
1721
1728
|
|
1722
1729
|
# Get the row of column data from the database.
|
@@ -1754,7 +1761,7 @@ module Sequel
|
|
1754
1761
|
@this = nil
|
1755
1762
|
@new = false
|
1756
1763
|
@modified = false
|
1757
|
-
pk ? _save_refresh :
|
1764
|
+
pk ? _save_refresh : _clear_changed_columns(:create)
|
1758
1765
|
after_create
|
1759
1766
|
true
|
1760
1767
|
end
|
@@ -1771,7 +1778,7 @@ module Sequel
|
|
1771
1778
|
cc.clear
|
1772
1779
|
else
|
1773
1780
|
columns_updated = _save_update_all_columns_hash
|
1774
|
-
|
1781
|
+
_clear_changed_columns(:update)
|
1775
1782
|
end
|
1776
1783
|
else # update only the specified columns
|
1777
1784
|
columns = Array(columns)
|
@@ -1798,7 +1805,7 @@ module Sequel
|
|
1798
1805
|
# can be overridden to avoid the refresh.
|
1799
1806
|
def _save_refresh
|
1800
1807
|
_save_set_values(_refresh_get(this.server?(:default)) || raise(NoExistingObject, "Record not found"))
|
1801
|
-
|
1808
|
+
_clear_changed_columns(:create)
|
1802
1809
|
end
|
1803
1810
|
|
1804
1811
|
# Set values to the provided hash. Called after a create,
|
@@ -26,6 +26,9 @@ module Sequel
|
|
26
26
|
# * Model.with_pk!
|
27
27
|
# * Model.[] # when argument is not hash or nil
|
28
28
|
# * many_to_one association method # without dynamic callback, when primary key matches
|
29
|
+
#
|
30
|
+
# You should not use this plugin if you are using sharding and there are different
|
31
|
+
# rows for the same primary key on different shards.
|
29
32
|
#
|
30
33
|
# Usage:
|
31
34
|
#
|
data/lib/sequel/plugins/dirty.rb
CHANGED
@@ -159,9 +159,9 @@ module Sequel
|
|
159
159
|
|
160
160
|
private
|
161
161
|
|
162
|
-
# Reset
|
163
|
-
def
|
164
|
-
reset_initial_values
|
162
|
+
# Reset initial values when clearing changed columns
|
163
|
+
def _clear_changed_columns(reason)
|
164
|
+
reset_initial_values if reason == :initialize || reason == :refresh
|
165
165
|
super
|
166
166
|
end
|
167
167
|
|
@@ -214,12 +214,6 @@ module Sequel
|
|
214
214
|
self
|
215
215
|
end
|
216
216
|
|
217
|
-
# Reset the initial values when initializing.
|
218
|
-
def initialize_set(h)
|
219
|
-
super
|
220
|
-
reset_initial_values
|
221
|
-
end
|
222
|
-
|
223
217
|
# Array holding column symbols that were not present initially. This is necessary
|
224
218
|
# to differentiate between values that were not present and values that were
|
225
219
|
# present but equal to nil.
|
@@ -113,6 +113,10 @@ module Sequel
|
|
113
113
|
# value, the attribute hash is ignored.
|
114
114
|
# :remove :: Allow disassociation of nested records (can remove the associated
|
115
115
|
# object from the parent object, but not destroy the associated object).
|
116
|
+
# :require_modification :: Whether to require modification of nested objects when
|
117
|
+
# updating or deleting them (checking that a single row was
|
118
|
+
# updated). By default, uses the default require_modification
|
119
|
+
# setting for the nested object.
|
116
120
|
# :transform :: A proc to transform attribute hashes before they are
|
117
121
|
# passed to associated object. Takes two arguments, the parent object and
|
118
122
|
# the attribute hash. Uses the return value as the new attribute hash.
|
@@ -282,6 +286,9 @@ module Sequel
|
|
282
286
|
obj = Array(public_send(reflection[:name])).find{|x| Array(x.pk).map(&:to_s) == pk}
|
283
287
|
end
|
284
288
|
if obj
|
289
|
+
unless (require_modification = meta[:require_modification]).nil?
|
290
|
+
obj.require_modification = require_modification
|
291
|
+
end
|
285
292
|
attributes = attributes.dup.delete_if{|k,v| str_keys.include? k.to_s}
|
286
293
|
if meta[:destroy] && klass.db.send(:typecast_value_boolean, attributes.delete(:_delete) || attributes.delete('_delete'))
|
287
294
|
nested_attributes_remove(meta, obj, :destroy=>true)
|
@@ -41,7 +41,9 @@ module Sequel
|
|
41
41
|
# Typecast values using #load_typecast when the values are retrieved
|
42
42
|
# from the database.
|
43
43
|
def call(values)
|
44
|
-
super.load_typecast
|
44
|
+
o = super.load_typecast
|
45
|
+
o.send(:_clear_changed_columns, :initialize)
|
46
|
+
o
|
45
47
|
end
|
46
48
|
|
47
49
|
# Freeze typecast on load columns when freezing model class.
|
@@ -63,7 +65,6 @@ module Sequel
|
|
63
65
|
set_column_value("#{c}=", v)
|
64
66
|
end
|
65
67
|
end
|
66
|
-
_changed_columns.clear
|
67
68
|
self
|
68
69
|
end
|
69
70
|
|
data/lib/sequel/version.rb
CHANGED
@@ -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 =
|
9
|
+
MINOR = 26
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
@@ -368,6 +368,14 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
|
|
368
368
|
@ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{Sequel[:ic_test][:b]=>4}).insert(1, 3, 4).must_be_nil
|
369
369
|
@ds.all.must_equal [{:a=>1, :b=>5, :c=>5, :c_is_unique=>false}]
|
370
370
|
end
|
371
|
+
|
372
|
+
it "Dataset#insert_conflict should support table aliases" do
|
373
|
+
@ds = @db[Sequel[:ic_test].as(:foo)]
|
374
|
+
@ds.insert(1, 2, 5)
|
375
|
+
proc{@ds.insert(1, 3, 4)}.must_raise Sequel::UniqueConstraintViolation
|
376
|
+
@ds.insert_conflict(:target=>:a, :update=>{:b=>Sequel[:foo][:c] + Sequel[:excluded][:c]}).insert(1, 7, 10)
|
377
|
+
@ds.all.must_equal [{:a=>1, :b=>15, :c=>5, :c_is_unique=>false}]
|
378
|
+
end
|
371
379
|
end if DB.server_version >= 90500
|
372
380
|
|
373
381
|
describe "A PostgreSQL database" do
|
@@ -3341,6 +3349,65 @@ describe 'PostgreSQL json type' do
|
|
3341
3349
|
@db.from(jo.each_text).select_order_map(:key).must_equal %w'a b'
|
3342
3350
|
@db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).gsub(' ', '').must_match(/\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/)
|
3343
3351
|
|
3352
|
+
if DB.server_version >= 120000 && json_type == :jsonb
|
3353
|
+
@db.get(jo.path_exists('$.b.d.e')).must_equal true
|
3354
|
+
@db.get(jo.path_exists('$.b.d.f')).must_equal false
|
3355
|
+
|
3356
|
+
@db.get(jo.path_exists!('$.b.d.e')).must_equal true
|
3357
|
+
@db.get(jo.path_exists!('$.b.d.f')).must_equal false
|
3358
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal true
|
3359
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal false
|
3360
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 2)).must_equal true
|
3361
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 4)).must_equal false
|
3362
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal true
|
3363
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal false
|
3364
|
+
|
3365
|
+
@db.get(jo.path_match('$.b.d.e')).must_be_nil
|
3366
|
+
@db.get(jo.path_match('$.b.d.f')).must_be_nil
|
3367
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match('$.b.d.e')).must_equal true
|
3368
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match('$.b.d.e')).must_equal false
|
3369
|
+
|
3370
|
+
proc{@db.get(jo.path_match!('$.b.d.e'))}.must_raise(Sequel::DatabaseError)
|
3371
|
+
proc{@db.get(jo.path_match!('$.b.d.f'))}.must_raise(Sequel::DatabaseError)
|
3372
|
+
@db.get(jo.path_match!('$.b.d.e', {}, true)).must_be_nil
|
3373
|
+
@db.get(jo.path_match!('$.b.d.f', {}, true)).must_be_nil
|
3374
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match!('$.b.d.e')).must_equal true
|
3375
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match!('$.b.d.e')).must_equal false
|
3376
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":2}')).must_equal true
|
3377
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":4}')).must_equal false
|
3378
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 2)).must_equal true
|
3379
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 4)).must_equal false
|
3380
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 2}, false)).must_equal true
|
3381
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 4}, true)).must_equal false
|
3382
|
+
|
3383
|
+
@db.get(jo.path_query_first('$.b.d.e')).must_equal 3
|
3384
|
+
@db.get(jo.path_query_first('$.b.d.f')).must_be_nil
|
3385
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal 3
|
3386
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":4}')).must_be_nil
|
3387
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 2)).must_equal 3
|
3388
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 4)).must_be_nil
|
3389
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal 3
|
3390
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_be_nil
|
3391
|
+
|
3392
|
+
@db.get(jo.path_query_array('$.b.d.e')).must_equal [3]
|
3393
|
+
@db.get(jo.path_query_array('$.b.d.f')).must_equal []
|
3394
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal [3]
|
3395
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal []
|
3396
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 2)).must_equal [3]
|
3397
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 4)).must_equal []
|
3398
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal [3]
|
3399
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal []
|
3400
|
+
|
3401
|
+
@db.from(jo.path_query('$.b.d.e').as(:a, [:b])).get(:b).must_equal 3
|
3402
|
+
@db.from(jo.path_query('$.b.d.f').as(:a, [:b])).get(:b).must_be_nil
|
3403
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":2}').as(:a, [:b])).get(:b).must_equal 3
|
3404
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":4}').as(:a, [:b])).get(:b).must_be_nil
|
3405
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 2).as(:a, [:b])).get(:b).must_equal 3
|
3406
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 4).as(:a, [:b])).get(:b).must_be_nil
|
3407
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 2}, true).as(:a, [:b])).get(:b).must_equal 3
|
3408
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 4}, false).as(:a, [:b])).get(:b).must_be_nil
|
3409
|
+
end
|
3410
|
+
|
3344
3411
|
Sequel.extension :pg_row_ops
|
3345
3412
|
@db.create_table!(:items) do
|
3346
3413
|
Integer :a
|
@@ -162,6 +162,39 @@ describe "Sequel::Plugins::Dirty" do
|
|
162
162
|
@o.save
|
163
163
|
@o.column_changes.must_equal({})
|
164
164
|
end
|
165
|
+
|
166
|
+
it "should work with the typecast_on_load plugin" do
|
167
|
+
@c.instance_variable_set(:@db_schema, :initial=>{:type=>:integer})
|
168
|
+
@c.plugin :typecast_on_load, :initial
|
169
|
+
|
170
|
+
@o = @c.call(:initial=>'1')
|
171
|
+
@o.column_changes.must_equal({})
|
172
|
+
@o.save
|
173
|
+
@o.previous_changes.must_equal({})
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should have column_changes work with the typecast_on_load in after hooks" do
|
177
|
+
@c.instance_variable_set(:@db_schema, :initial=>{:type=>:integer})
|
178
|
+
@c.plugin :typecast_on_load, :initial
|
179
|
+
|
180
|
+
@o = @c.new
|
181
|
+
@o.initial = 1
|
182
|
+
@o.column_changes.must_equal({:initial=>[nil, 1]})
|
183
|
+
column_changes_in_after_save = nil
|
184
|
+
@o.define_singleton_method(:after_save) do
|
185
|
+
column_changes_in_after_save = column_changes
|
186
|
+
super()
|
187
|
+
end
|
188
|
+
@db.fetch = {:initial=>1}
|
189
|
+
@o.save
|
190
|
+
column_changes_in_after_save.must_equal({:initial=>[nil, 1]})
|
191
|
+
|
192
|
+
@o.initial = 2
|
193
|
+
@o.column_changes.must_equal({:initial=>[1, 2]})
|
194
|
+
@o.save
|
195
|
+
column_changes_in_after_save.must_equal({:initial=>[1, 2]})
|
196
|
+
@o.previous_changes.must_equal({:initial=>[1, 2]})
|
197
|
+
end
|
165
198
|
end
|
166
199
|
|
167
200
|
describe "with existing instance" do
|
@@ -492,6 +492,54 @@ describe "NestedAttributes plugin" do
|
|
492
492
|
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 10)"]
|
493
493
|
end
|
494
494
|
|
495
|
+
it "should raise a NoExistingObject error if object to be updated no longer exists, if the :require_modification=>true option is used" do
|
496
|
+
@Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
|
497
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
498
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
499
|
+
ar.associations[:albums] = [al]
|
500
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
|
501
|
+
@db.sqls.must_equal []
|
502
|
+
@db.numrows = [1, 0]
|
503
|
+
proc{ar.save}.must_raise Sequel::NoExistingObject
|
504
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should not raise an Error if object to be updated no longer exists, if the :require_modification=>false option is used" do
|
508
|
+
@Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
|
509
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
510
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
511
|
+
ar.associations[:albums] = [al]
|
512
|
+
ar.set(:albums_attributes=>[{:id=>10, :name=>'L'}])
|
513
|
+
@db.sqls.must_equal []
|
514
|
+
@db.numrows = [1, 0]
|
515
|
+
ar.save
|
516
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "UPDATE albums SET name = 'L' WHERE (id = 10)"]
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should raise a NoExistingObject error if object to be deleted no longer exists, if the :require_modification=>true option is used" do
|
520
|
+
@Artist.nested_attributes :albums, :require_modification=>true, :destroy=>true
|
521
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
522
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
523
|
+
ar.associations[:albums] = [al]
|
524
|
+
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
525
|
+
@db.sqls.must_equal []
|
526
|
+
@db.numrows = [1, 0]
|
527
|
+
proc{ar.save}.must_raise Sequel::NoExistingObject
|
528
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
529
|
+
end
|
530
|
+
|
531
|
+
it "should not raise an Error if object to be deleted no longer exists, if the :require_modification=>false option is used" do
|
532
|
+
@Artist.nested_attributes :albums, :require_modification=>false, :destroy=>true
|
533
|
+
al = @Album.load(:id=>10, :name=>'Al')
|
534
|
+
ar = @Artist.load(:id=>20, :name=>'Ar')
|
535
|
+
ar.associations[:albums] = [al]
|
536
|
+
ar.set(:albums_attributes=>[{:id=>10, :_delete=>'t'}])
|
537
|
+
@db.sqls.must_equal []
|
538
|
+
@db.numrows = [1, 0]
|
539
|
+
ar.save
|
540
|
+
@db.sqls.must_equal ["UPDATE artists SET name = 'Ar' WHERE (id = 20)", "DELETE FROM albums WHERE (id = 10)"]
|
541
|
+
end
|
542
|
+
|
495
543
|
it "should not attempt to validate nested attributes twice for one_to_many associations when creating them" do
|
496
544
|
@Artist.nested_attributes :albums
|
497
545
|
validated = []
|
@@ -286,4 +286,71 @@ describe "Sequel::Postgres::JSONOp" do
|
|
286
286
|
it "should allow transforming JSONBHash instances into ArrayOp instances" do
|
287
287
|
@db.literal(Sequel.pg_jsonb('a'=>1).op['a']).must_equal "('{\"a\":1}'::jsonb -> 'a')"
|
288
288
|
end
|
289
|
+
|
290
|
+
it "#path_exists should use the @? operator" do
|
291
|
+
@l[@jb.path_exists('$')].must_equal "(j @? '$')"
|
292
|
+
end
|
293
|
+
|
294
|
+
it "#path_exists result should be a boolean expression" do
|
295
|
+
@jb.path_exists('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
296
|
+
end
|
297
|
+
|
298
|
+
it "#path_match should use the @@ operator" do
|
299
|
+
@l[@jb.path_match('$')].must_equal "(j @@ '$')"
|
300
|
+
end
|
301
|
+
|
302
|
+
it "#path_match result should be a boolean expression" do
|
303
|
+
@jb.path_match('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
304
|
+
end
|
305
|
+
|
306
|
+
it "#path_exists! should use the jsonb_path_exists function" do
|
307
|
+
@l[@jb.path_exists!('$')].must_equal "jsonb_path_exists(j, '$')"
|
308
|
+
@l[@jb.path_exists!('$', '{"x":2}')].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}')"
|
309
|
+
@l[@jb.path_exists!('$', x: 2)].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}')"
|
310
|
+
@l[@jb.path_exists!('$', {x: 2}, true)].must_equal "jsonb_path_exists(j, '$', '{\"x\":2}', true)"
|
311
|
+
end
|
312
|
+
|
313
|
+
it "#path_exists! result should be a boolean expression" do
|
314
|
+
@jb.path_exists!('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
315
|
+
end
|
316
|
+
|
317
|
+
it "#path_match! should use the jsonb_path_match function" do
|
318
|
+
@l[@jb.path_match!('$')].must_equal "jsonb_path_match(j, '$')"
|
319
|
+
@l[@jb.path_match!('$', '{"x":2}')].must_equal "jsonb_path_match(j, '$', '{\"x\":2}')"
|
320
|
+
@l[@jb.path_match!('$', x: 2)].must_equal "jsonb_path_match(j, '$', '{\"x\":2}')"
|
321
|
+
@l[@jb.path_match!('$', {x: 2}, true)].must_equal "jsonb_path_match(j, '$', '{\"x\":2}', true)"
|
322
|
+
end
|
323
|
+
|
324
|
+
it "#path_match! result should be a boolean expression" do
|
325
|
+
@jb.path_match!('$').must_be_kind_of Sequel::SQL::BooleanExpression
|
326
|
+
end
|
327
|
+
|
328
|
+
it "#path_query should use the jsonb_path_query function" do
|
329
|
+
@l[@jb.path_query('$')].must_equal "jsonb_path_query(j, '$')"
|
330
|
+
@l[@jb.path_query('$', '{"x":2}')].must_equal "jsonb_path_query(j, '$', '{\"x\":2}')"
|
331
|
+
@l[@jb.path_query('$', x: 2)].must_equal "jsonb_path_query(j, '$', '{\"x\":2}')"
|
332
|
+
@l[@jb.path_query('$', {x: 2}, true)].must_equal "jsonb_path_query(j, '$', '{\"x\":2}', true)"
|
333
|
+
end
|
334
|
+
|
335
|
+
it "#path_query_array should use the jsonb_path_query_array function" do
|
336
|
+
@l[@jb.path_query_array('$')].must_equal "jsonb_path_query_array(j, '$')"
|
337
|
+
@l[@jb.path_query_array('$', '{"x":2}')].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}')"
|
338
|
+
@l[@jb.path_query_array('$', x: 2)].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}')"
|
339
|
+
@l[@jb.path_query_array('$', {x: 2}, true)].must_equal "jsonb_path_query_array(j, '$', '{\"x\":2}', true)"
|
340
|
+
end
|
341
|
+
|
342
|
+
it "#path_query_array result should be a JSONBOp" do
|
343
|
+
@l[@jb.path_query_array('$').path_query_array('$')].must_equal "jsonb_path_query_array(jsonb_path_query_array(j, '$'), '$')"
|
344
|
+
end
|
345
|
+
|
346
|
+
it "#path_query_first should use the jsonb_path_query_first function" do
|
347
|
+
@l[@jb.path_query_first('$')].must_equal "jsonb_path_query_first(j, '$')"
|
348
|
+
@l[@jb.path_query_first('$', '{"x":2}')].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}')"
|
349
|
+
@l[@jb.path_query_first('$', x: 2)].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}')"
|
350
|
+
@l[@jb.path_query_first('$', {x: 2}, true)].must_equal "jsonb_path_query_first(j, '$', '{\"x\":2}', true)"
|
351
|
+
end
|
352
|
+
|
353
|
+
it "#path_query_first result should be a JSONBOp" do
|
354
|
+
@l[@jb.path_query_first('$').path_query_first('$')].must_equal "jsonb_path_query_first(jsonb_path_query_first(j, '$'), '$')"
|
355
|
+
end
|
289
356
|
end
|
@@ -136,6 +136,20 @@ describe "pg_range extension" do
|
|
136
136
|
s[1][1][:ruby_default].must_equal Sequel::Postgres::PGRange.new(1, 5, :exclude_end=>true, :db_type=>'int4range')
|
137
137
|
end
|
138
138
|
|
139
|
+
it "should work correctly in hashes" do
|
140
|
+
h = Hash.new(1)
|
141
|
+
h[@R.new(1, 2)] = 2
|
142
|
+
h[@R.new(nil, nil, :empty => true)] = 3
|
143
|
+
h[@R.new(1, 2)].must_equal 2
|
144
|
+
h[@R.new(1, 3)].must_equal 1
|
145
|
+
h[@R.new(2, 2)].must_equal 1
|
146
|
+
h[@R.new(1, 2, :exclude_begin => true)].must_equal 1
|
147
|
+
h[@R.new(1, 2, :exclude_end => true)].must_equal 1
|
148
|
+
h[@R.new(1, 2, :db_type => :int)].must_equal 1
|
149
|
+
h[@R.new(nil, nil, :empty => true)].must_equal 3
|
150
|
+
h[@R.new(nil, nil, :empty => true, :db_type => :int)].must_equal 1
|
151
|
+
end
|
152
|
+
|
139
153
|
describe "database typecasting" do
|
140
154
|
before do
|
141
155
|
@o = @R.new(1, 2, :db_type=>'int4range')
|
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.
|
4
|
+
version: 5.26.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
|
+
date: 2019-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -223,6 +223,7 @@ extra_rdoc_files:
|
|
223
223
|
- doc/release_notes/5.23.0.txt
|
224
224
|
- doc/release_notes/5.24.0.txt
|
225
225
|
- doc/release_notes/5.25.0.txt
|
226
|
+
- doc/release_notes/5.26.0.txt
|
226
227
|
files:
|
227
228
|
- CHANGELOG
|
228
229
|
- MIT-LICENSE
|
@@ -319,6 +320,7 @@ files:
|
|
319
320
|
- doc/release_notes/5.23.0.txt
|
320
321
|
- doc/release_notes/5.24.0.txt
|
321
322
|
- doc/release_notes/5.25.0.txt
|
323
|
+
- doc/release_notes/5.26.0.txt
|
322
324
|
- doc/release_notes/5.3.0.txt
|
323
325
|
- doc/release_notes/5.4.0.txt
|
324
326
|
- doc/release_notes/5.5.0.txt
|