sequel 5.73.0 → 5.75.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 +22 -0
- data/doc/migration.rdoc +14 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/lib/sequel/adapters/jdbc/db2.rb +0 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +4 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/postgres.rb +4 -3
- data/lib/sequel/adapters/shared/mysql.rb +21 -1
- data/lib/sequel/adapters/shared/postgres.rb +54 -0
- data/lib/sequel/adapters/shared/sqlite.rb +0 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/migration.rb +47 -8
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3047129164c9cdffee31414419904329e410db71e7e4243c0549614498a2b0d4
|
4
|
+
data.tar.gz: 9cc84ed8e9dba53aac7175be04699089932566e4015aed7a465c43552808c8b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15ed5c13e4793191546cd452aa5865e65fd7b3bb0458ffdb674d526398b7c9446e2da0f43a339daa3f1dc256eb551f0b773ac9ffc192b4d19f389d0c7e296d98
|
7
|
+
data.tar.gz: 1f25851b50857fbbf8378216794f26d0d8e9baa247f008feac466a218ae6b0777249252b6d05756e075096c5312b9d3b1334b7c4bc1884251e7b892cc17ae7c6
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
=== 5.75.0 (2023-12-01)
|
2
|
+
|
3
|
+
* Make any_not_empty? extension support passing pattern argument to any? (jeremyevans) (#2100)
|
4
|
+
|
5
|
+
* Respect :skip_transaction option in PostgreSQL Dataset#paged_each (jeremyevans) (#2097)
|
6
|
+
|
7
|
+
* Add TimestampMigrator.run_single to run a single migration file up or down (opya, jeremyevans) (#2093)
|
8
|
+
|
9
|
+
* Support INSERT RETURNING on MariaDB 10.5+, and use it when saving new model objects (jeremyevans)
|
10
|
+
|
11
|
+
* Add Database#{defer,immediate}_constraints on PostgreSQL for changing handling of deferrable constraints in a transaction (jeremyevans)
|
12
|
+
|
13
|
+
=== 5.74.0 (2023-11-01)
|
14
|
+
|
15
|
+
* Make generated columns show up in Database#schema when using SQLite 3.37+ (jeremyevans) (#2087)
|
16
|
+
|
17
|
+
* Add revert method for Sequel.migration blocks, to revert changes inside the block on up, and apply the changes on down (jeremyevans)
|
18
|
+
|
19
|
+
* Re-add is_json and is_not_json methods to the pg_json_ops extension, as the support was re-added in PostgreSQL 16 (jeremyevans)
|
20
|
+
|
21
|
+
* Avoid infinite loop when handling exceptions with a cause loop in jdbc adapter (jeremyevans)
|
22
|
+
|
1
23
|
=== 5.73.0 (2023-10-01)
|
2
24
|
|
3
25
|
* Handle disconnect errors in ibmdb and jdbc/db2 adapters (jeremyevans) (#2083)
|
data/doc/migration.rdoc
CHANGED
@@ -90,6 +90,20 @@ the following methods:
|
|
90
90
|
|
91
91
|
If you use any other methods, you should create your own +down+ block.
|
92
92
|
|
93
|
+
To revert a migration created with +change+, you can copy the migration to a new file, and
|
94
|
+
replace +change+ with +revert+. For example, if you no longer need the artists table, you
|
95
|
+
can use the following migration. This will drop the artists table when migrating up, and
|
96
|
+
recreate it when migrating down:
|
97
|
+
|
98
|
+
Sequel.migration do
|
99
|
+
revert do
|
100
|
+
create_table(:artists) do
|
101
|
+
primary_key :id
|
102
|
+
String :name, null: false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
93
107
|
In normal usage, when Sequel's migrator runs, it runs the +up+ blocks for all
|
94
108
|
migrations that have not yet been applied. However, you can use the <tt>-M</tt>
|
95
109
|
switch to specify the version to which to migrate, and if it is lower than the
|
@@ -0,0 +1,45 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Sequel.migration blocks now support a revert method, which reverts
|
4
|
+
the changes in the block on up, and applies them on down. So if
|
5
|
+
you have a migration such as:
|
6
|
+
|
7
|
+
Sequel.migration do
|
8
|
+
change do
|
9
|
+
create_table :table do
|
10
|
+
# ...
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
and you later want to add a migration that drops the table, you
|
16
|
+
can use:
|
17
|
+
|
18
|
+
Sequel.migration do
|
19
|
+
revert do
|
20
|
+
create_table :table do
|
21
|
+
# ...
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
This will drop the table when migrating up, and create a table
|
27
|
+
with the given schema when migrating down.
|
28
|
+
|
29
|
+
* is_json and is_not_json methods have been added to the pg_json_ops
|
30
|
+
extension, for the IS [NOT] JSON operator supported in PostgreSQL
|
31
|
+
16+. These were previously added in Sequel 5.59.0, and removed
|
32
|
+
in Sequel 5.61.0 as support was removed in PostgreSQL 15 beta 4.
|
33
|
+
PostgreSQL 16 shipped with support for them, so support has been
|
34
|
+
recommitted to Sequel.
|
35
|
+
|
36
|
+
= Other Improvements
|
37
|
+
|
38
|
+
* SQLite generated columns now show up in Database#schema when using
|
39
|
+
SQLite 3.37+.
|
40
|
+
|
41
|
+
* Sequel now attempts to avoid an infinite loop in pathlogical cases
|
42
|
+
in the jdbc adapter, where the exception cause chain has a loop.
|
43
|
+
Additionally, if an exception is already recognized as a disconnect,
|
44
|
+
or an exception already responds to a getSQLState method, Sequel no
|
45
|
+
longer looks at the causes of the exception.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Database#{defer,immediate}_constraints methods have been added on
|
4
|
+
PostgreSQL for changing handling of deferrable constraints inside
|
5
|
+
a transaction. defer_constraints sets deferrable constraints to
|
6
|
+
be deferred (not checked until transaction commit), and
|
7
|
+
immediate_constraints sets deferrable constraints to be checked
|
8
|
+
as part of the related query, and any already deferred constraint
|
9
|
+
checks to be applied immediately. You can pass the :constraints
|
10
|
+
option to only apply the changes to specific constraints.
|
11
|
+
|
12
|
+
* TimestampMigrator.run_single has been added, to migrate a single
|
13
|
+
migration up or down.
|
14
|
+
|
15
|
+
= Other Improvements
|
16
|
+
|
17
|
+
* INSERT RETURNING is now supported on MariaDB 10.5+, and used
|
18
|
+
automatically when saving new model objects. Note that this
|
19
|
+
is not supported when using the jdbc adapter, because the
|
20
|
+
jdbc-mysql driver doesn't support it. A jdbc/mariadb adapter
|
21
|
+
could be added, as it's likely recent versions of the
|
22
|
+
jdbc-mariadb driver would support it, but the jdbc-mariadb gem
|
23
|
+
hasn't been updated in over 4 years. Talk to the jdbc-mariadb
|
24
|
+
gem maintainers if you want to use this feature with the jdbc
|
25
|
+
adapter.
|
26
|
+
|
27
|
+
* The Dataset#paged_each optimization in the postgres adapter
|
28
|
+
now respects the :skip_transaction option, making it the
|
29
|
+
same as the :hold option. Note that this has effects beyond
|
30
|
+
just skipping the transaction, but non-HOLD cursors are only
|
31
|
+
supported inside transactions.
|
32
|
+
|
33
|
+
* The any_not_empty? extension's Dataset#any? method now supports
|
34
|
+
an argument, passing it to Enumerable#any? (which has supported
|
35
|
+
an argument since Ruby 2.5).
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -396,11 +396,16 @@ module Sequel
|
|
396
396
|
|
397
397
|
def database_exception_sqlstate(exception, opts)
|
398
398
|
if database_exception_use_sqlstates?
|
399
|
-
|
400
|
-
exception = exception.cause
|
401
|
-
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
402
|
-
end
|
399
|
+
_database_exception_sqlstate(exception, opts)
|
403
400
|
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def _database_exception_sqlstate(exception, opts)
|
404
|
+
16.times do
|
405
|
+
return exception.getSQLState if exception.respond_to?(:getSQLState)
|
406
|
+
break unless exception.respond_to?(:cause) && (exception = exception.cause)
|
407
|
+
end
|
408
|
+
|
404
409
|
nil
|
405
410
|
end
|
406
411
|
|
@@ -415,8 +420,7 @@ module Sequel
|
|
415
420
|
|
416
421
|
# Raise a disconnect error if the SQL state of the cause of the exception indicates so.
|
417
422
|
def disconnect_error?(exception, opts)
|
418
|
-
|
419
|
-
super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
|
423
|
+
super || (_database_exception_sqlstate(exception, opts) =~ /^08/)
|
420
424
|
end
|
421
425
|
|
422
426
|
# Execute the prepared statement. If the provided name is a
|
@@ -672,6 +672,7 @@ module Sequel
|
|
672
672
|
# cursor usage.
|
673
673
|
# :rows_per_fetch :: The number of rows per fetch (default 1000). Higher
|
674
674
|
# numbers result in fewer queries but greater memory use.
|
675
|
+
# :skip_transaction :: Same as :hold, but :hold takes priority.
|
675
676
|
#
|
676
677
|
# Usage:
|
677
678
|
#
|
@@ -764,13 +765,13 @@ module Sequel
|
|
764
765
|
|
765
766
|
# Use a cursor to fetch groups of records at a time, yielding them to the block.
|
766
767
|
def cursor_fetch_rows(sql)
|
767
|
-
server_opts = {:server=>@opts[:server] || :read_only}
|
768
768
|
cursor = @opts[:cursor]
|
769
|
-
hold = cursor[:
|
769
|
+
hold = cursor.fetch(:hold){cursor[:skip_transaction]}
|
770
|
+
server_opts = {:server=>@opts[:server] || :read_only, :skip_transaction=>hold}
|
770
771
|
cursor_name = quote_identifier(cursor[:cursor_name] || 'sequel_cursor')
|
771
772
|
rows_per_fetch = cursor[:rows_per_fetch].to_i
|
772
773
|
|
773
|
-
db.
|
774
|
+
db.transaction(server_opts) do
|
774
775
|
begin
|
775
776
|
execute_ddl("DECLARE #{cursor_name} NO SCROLL CURSOR WITH#{'OUT' unless hold} HOLD FOR #{sql}", server_opts)
|
776
777
|
rows_per_fetch = 1000 if rows_per_fetch <= 0
|
@@ -646,7 +646,7 @@ module Sequel
|
|
646
646
|
MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
|
647
647
|
|
648
648
|
Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
|
649
|
-
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
|
649
|
+
Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update returning')
|
650
650
|
Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having window compounds order limit lock')
|
651
651
|
Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
|
652
652
|
|
@@ -774,6 +774,21 @@ module Sequel
|
|
774
774
|
clone(:insert_ignore=>true)
|
775
775
|
end
|
776
776
|
|
777
|
+
# Support insert select for associations, so that the model code can use
|
778
|
+
# returning instead of a separate query.
|
779
|
+
def insert_select(*values)
|
780
|
+
return unless supports_insert_select?
|
781
|
+
# Handle case where query does not return a row
|
782
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
783
|
+
end
|
784
|
+
|
785
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
786
|
+
# unless the RETURNING clause is already present.
|
787
|
+
def insert_select_sql(*values)
|
788
|
+
ds = opts[:returning] ? self : returning
|
789
|
+
ds.insert_sql(*values)
|
790
|
+
end
|
791
|
+
|
777
792
|
# Sets up the insert methods to use ON DUPLICATE KEY UPDATE
|
778
793
|
# If you pass no arguments, ALL fields will be
|
779
794
|
# updated with the new values. If you pass the fields you
|
@@ -871,6 +886,11 @@ module Sequel
|
|
871
886
|
true
|
872
887
|
end
|
873
888
|
|
889
|
+
# MariaDB 10.5.0 supports INSERT RETURNING.
|
890
|
+
def supports_returning?(type)
|
891
|
+
(type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
|
892
|
+
end
|
893
|
+
|
874
894
|
# MySQL 8+ supports SKIP LOCKED.
|
875
895
|
def supports_skip_locked?
|
876
896
|
!db.mariadb? && db.server_version >= 80000
|
@@ -498,6 +498,25 @@ module Sequel
|
|
498
498
|
:postgres
|
499
499
|
end
|
500
500
|
|
501
|
+
# For constraints that are deferrable, defer constraints until
|
502
|
+
# transaction commit. Options:
|
503
|
+
#
|
504
|
+
# :constraints :: An identifier of the constraint, or an array of
|
505
|
+
# identifiers for constraints, to apply this
|
506
|
+
# change to specific constraints.
|
507
|
+
# :server :: The server/shard on which to run the query.
|
508
|
+
#
|
509
|
+
# Examples:
|
510
|
+
#
|
511
|
+
# DB.defer_constraints
|
512
|
+
# # SET CONSTRAINTS ALL DEFERRED
|
513
|
+
#
|
514
|
+
# DB.defer_constraints(constraints: [:c1, Sequel[:sc][:c2]])
|
515
|
+
# # SET CONSTRAINTS "c1", "sc"."s2" DEFERRED
|
516
|
+
def defer_constraints(opts=OPTS)
|
517
|
+
_set_constraints(' DEFERRED', opts)
|
518
|
+
end
|
519
|
+
|
501
520
|
# Use PostgreSQL's DO syntax to execute an anonymous code block. The code should
|
502
521
|
# be the literal code string to use in the underlying procedural language. Options:
|
503
522
|
#
|
@@ -611,6 +630,24 @@ module Sequel
|
|
611
630
|
super
|
612
631
|
end
|
613
632
|
|
633
|
+
# Immediately apply deferrable constraints.
|
634
|
+
#
|
635
|
+
# :constraints :: An identifier of the constraint, or an array of
|
636
|
+
# identifiers for constraints, to apply this
|
637
|
+
# change to specific constraints.
|
638
|
+
# :server :: The server/shard on which to run the query.
|
639
|
+
#
|
640
|
+
# Examples:
|
641
|
+
#
|
642
|
+
# DB.immediate_constraints
|
643
|
+
# # SET CONSTRAINTS ALL IMMEDIATE
|
644
|
+
#
|
645
|
+
# DB.immediate_constraints(constraints: [:c1, Sequel[:sc][:c2]])
|
646
|
+
# # SET CONSTRAINTS "c1", "sc"."s2" IMMEDIATE
|
647
|
+
def immediate_constraints(opts=OPTS)
|
648
|
+
_set_constraints(' IMMEDIATE', opts)
|
649
|
+
end
|
650
|
+
|
614
651
|
# Use the pg_* system tables to determine indexes on a table
|
615
652
|
def indexes(table, opts=OPTS)
|
616
653
|
m = output_identifier_meth
|
@@ -1038,6 +1075,23 @@ module Sequel
|
|
1038
1075
|
end
|
1039
1076
|
end
|
1040
1077
|
|
1078
|
+
# Internals of defer_constraints/immediate_constraints
|
1079
|
+
def _set_constraints(type, opts)
|
1080
|
+
execute_ddl(_set_constraints_sql(type, opts), opts)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# SQL to use for SET CONSTRAINTS
|
1084
|
+
def _set_constraints_sql(type, opts)
|
1085
|
+
sql = String.new
|
1086
|
+
sql << "SET CONSTRAINTS "
|
1087
|
+
if constraints = opts[:constraints]
|
1088
|
+
dataset.send(:source_list_append, sql, Array(constraints))
|
1089
|
+
else
|
1090
|
+
sql << "ALL"
|
1091
|
+
end
|
1092
|
+
sql << type
|
1093
|
+
end
|
1094
|
+
|
1041
1095
|
def alter_table_add_column_sql(table, op)
|
1042
1096
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
1043
1097
|
end
|
@@ -504,7 +504,6 @@ module Sequel
|
|
504
504
|
# table_xinfo PRAGMA used, remove hidden columns
|
505
505
|
# that are not generated columns
|
506
506
|
if row[:generated] = (row.delete(:hidden) != 0)
|
507
|
-
next unless row[:type].end_with?(' GENERATED ALWAYS')
|
508
507
|
row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
|
509
508
|
end
|
510
509
|
end
|
@@ -159,6 +159,19 @@ module Sequel
|
|
159
159
|
migration.up = block
|
160
160
|
migration.down = MigrationReverser.new.reverse(&block)
|
161
161
|
end
|
162
|
+
|
163
|
+
# Creates a revert migration. This is the same as creating
|
164
|
+
# the same block with +down+, but it also calls the block and attempts
|
165
|
+
# to create a +up+ block that will reverse the changes made by
|
166
|
+
# the block. This is designed to revert the changes in the
|
167
|
+
# provided block.
|
168
|
+
#
|
169
|
+
# There are no guarantees that this will work perfectly
|
170
|
+
# in all cases, but it works for some simple cases.
|
171
|
+
def revert(&block)
|
172
|
+
migration.down = block
|
173
|
+
migration.up = MigrationReverser.new.reverse(&block)
|
174
|
+
end
|
162
175
|
end
|
163
176
|
|
164
177
|
# Handles the reversing of reversible migrations. Basically records
|
@@ -680,6 +693,13 @@ module Sequel
|
|
680
693
|
@migration_tuples = get_migration_tuples
|
681
694
|
end
|
682
695
|
|
696
|
+
# Apply the migration in the given file path. See Migrator.run for the
|
697
|
+
# available options. Additionally, this method supports the :direction
|
698
|
+
# option for whether to run the migration up (default) or down.
|
699
|
+
def self.run_single(db, path, opts=OPTS)
|
700
|
+
new(db, File.dirname(path), opts).run_single(path, opts[:direction] || :up)
|
701
|
+
end
|
702
|
+
|
683
703
|
# The timestamp migrator is current if there are no migrations to apply
|
684
704
|
# in either direction.
|
685
705
|
def is_current?
|
@@ -689,20 +709,39 @@ module Sequel
|
|
689
709
|
# Apply all migration tuples on the database
|
690
710
|
def run
|
691
711
|
migration_tuples.each do |m, f, direction|
|
692
|
-
|
693
|
-
db.log_info("Begin applying migration #{f}, direction: #{direction}")
|
694
|
-
checked_transaction(m) do
|
695
|
-
m.apply(db, direction)
|
696
|
-
fi = f.downcase
|
697
|
-
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
698
|
-
end
|
699
|
-
db.log_info("Finished applying migration #{f}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
712
|
+
apply_migration(m, f, direction)
|
700
713
|
end
|
701
714
|
nil
|
702
715
|
end
|
703
716
|
|
717
|
+
# Apply single migration tuple at the given path with the given direction
|
718
|
+
# on the database.
|
719
|
+
def run_single(path, direction)
|
720
|
+
migration = load_migration_file(path)
|
721
|
+
file_name = File.basename(path)
|
722
|
+
already_applied = applied_migrations.include?(file_name.downcase)
|
723
|
+
|
724
|
+
return if direction == :up ? already_applied : !already_applied
|
725
|
+
|
726
|
+
apply_migration(migration, file_name, direction)
|
727
|
+
nil
|
728
|
+
end
|
729
|
+
|
704
730
|
private
|
705
731
|
|
732
|
+
# Apply a single migration with the given filename in the given direction.
|
733
|
+
def apply_migration(migration, file_name, direction)
|
734
|
+
fi = file_name.downcase
|
735
|
+
t = Time.now
|
736
|
+
|
737
|
+
db.log_info("Begin applying migration #{file_name}, direction: #{direction}")
|
738
|
+
checked_transaction(migration) do
|
739
|
+
migration.apply(db, direction)
|
740
|
+
direction == :up ? ds.insert(column=>fi) : ds.where(column=>fi).delete
|
741
|
+
end
|
742
|
+
db.log_info("Finished applying migration #{file_name}, direction: #{direction}, took #{sprintf('%0.6f', Time.now - t)} seconds")
|
743
|
+
end
|
744
|
+
|
706
745
|
# Convert the schema_info table to the new schema_migrations table format,
|
707
746
|
# using the version of the schema_info table and the current migration files.
|
708
747
|
def convert_from_schema_info
|
@@ -123,6 +123,15 @@
|
|
123
123
|
# c = Sequel.pg_jsonb_op(:c)
|
124
124
|
# DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
|
125
125
|
#
|
126
|
+
# On PostgreSQL 16+, the <tt>IS [NOT] JSON</tt> operator is supported:
|
127
|
+
#
|
128
|
+
# j.is_json # j IS JSON
|
129
|
+
# j.is_json(type: :object) # j IS JSON OBJECT
|
130
|
+
# j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
|
131
|
+
# j.is_not_json # j IS NOT JSON
|
132
|
+
# j.is_not_json(type: :array) # j IS NOT JSON ARRAY
|
133
|
+
# j.is_not_json(unique: true) # j IS NOT JSON WITH UNIQUE
|
134
|
+
#
|
126
135
|
# If you are also using the pg_json extension, you should load it before
|
127
136
|
# loading this extension. Doing so will allow you to use the #op method on
|
128
137
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -151,6 +160,18 @@ module Sequel
|
|
151
160
|
GET_PATH = ["(".freeze, " #> ".freeze, ")".freeze].freeze
|
152
161
|
GET_PATH_TEXT = ["(".freeze, " #>> ".freeze, ")".freeze].freeze
|
153
162
|
|
163
|
+
IS_JSON = ["(".freeze, " IS JSON".freeze, "".freeze, ")".freeze].freeze
|
164
|
+
IS_NOT_JSON = ["(".freeze, " IS NOT JSON".freeze, "".freeze, ")".freeze].freeze
|
165
|
+
EMPTY_STRING = Sequel::LiteralString.new('').freeze
|
166
|
+
WITH_UNIQUE = Sequel::LiteralString.new(' WITH UNIQUE').freeze
|
167
|
+
IS_JSON_MAP = {
|
168
|
+
nil => EMPTY_STRING,
|
169
|
+
:value => Sequel::LiteralString.new(' VALUE').freeze,
|
170
|
+
:scalar => Sequel::LiteralString.new(' SCALAR').freeze,
|
171
|
+
:object => Sequel::LiteralString.new(' OBJECT').freeze,
|
172
|
+
:array => Sequel::LiteralString.new(' ARRAY').freeze
|
173
|
+
}.freeze
|
174
|
+
|
154
175
|
# Get JSON array element or object field as json. If an array is given,
|
155
176
|
# gets the object at the specified path.
|
156
177
|
#
|
@@ -233,6 +254,30 @@ module Sequel
|
|
233
254
|
end
|
234
255
|
end
|
235
256
|
|
257
|
+
# Return whether the json object can be parsed as JSON.
|
258
|
+
#
|
259
|
+
# Options:
|
260
|
+
# :type :: Check whether the json object can be parsed as a specific type
|
261
|
+
# of JSON (:value, :scalar, :object, :array).
|
262
|
+
# :unique :: Check JSON objects for unique keys.
|
263
|
+
#
|
264
|
+
# json_op.is_json # json IS JSON
|
265
|
+
# json_op.is_json(type: :object) # json IS JSON OBJECT
|
266
|
+
# json_op.is_json(unique: true) # json IS JSON WITH UNIQUE
|
267
|
+
def is_json(opts=OPTS)
|
268
|
+
_is_json(IS_JSON, opts)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Return whether the json object cannot be parsed as JSON. The opposite
|
272
|
+
# of #is_json. See #is_json for options.
|
273
|
+
#
|
274
|
+
# json_op.is_not_json # json IS NOT JSON
|
275
|
+
# json_op.is_not_json(type: :object) # json IS NOT JSON OBJECT
|
276
|
+
# json_op.is_not_json(unique: true) # json IS NOT JSON WITH UNIQUE
|
277
|
+
def is_not_json(opts=OPTS)
|
278
|
+
_is_json(IS_NOT_JSON, opts)
|
279
|
+
end
|
280
|
+
|
236
281
|
# Returns a set of keys AS text in the json object.
|
237
282
|
#
|
238
283
|
# json_op.keys # json_object_keys(json)
|
@@ -286,6 +331,13 @@ module Sequel
|
|
286
331
|
|
287
332
|
private
|
288
333
|
|
334
|
+
# Internals of IS [NOT] JSON support
|
335
|
+
def _is_json(lit_array, opts)
|
336
|
+
raise Error, "invalid is_json :type option: #{opts[:type].inspect}" unless type = IS_JSON_MAP[opts[:type]]
|
337
|
+
unique = opts[:unique] ? WITH_UNIQUE : EMPTY_STRING
|
338
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(lit_array, [self, type, unique]))
|
339
|
+
end
|
340
|
+
|
289
341
|
# Return a placeholder literal with the given str and args, wrapped
|
290
342
|
# in an JSONOp or JSONBOp, used by operators that return json or jsonb.
|
291
343
|
def json_op(str, args)
|
@@ -325,7 +325,7 @@ module Sequel
|
|
325
325
|
# DB.alter_table(:ce_test) do
|
326
326
|
# c = Sequel[:encrypted_column_name]
|
327
327
|
# add_constraint(:enc_base64) do
|
328
|
-
# octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65
|
328
|
+
# octet_length(decode(regexp_replace(regexp_replace(c, '_', '/', 'g'), '-', '+', 'g'), 'base64')) >= 65
|
329
329
|
# end
|
330
330
|
# end
|
331
331
|
#
|
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 = 75
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
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.75.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: 2023-
|
11
|
+
date: 2023-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -220,6 +220,8 @@ extra_rdoc_files:
|
|
220
220
|
- doc/release_notes/5.71.0.txt
|
221
221
|
- doc/release_notes/5.72.0.txt
|
222
222
|
- doc/release_notes/5.73.0.txt
|
223
|
+
- doc/release_notes/5.74.0.txt
|
224
|
+
- doc/release_notes/5.75.0.txt
|
223
225
|
- doc/release_notes/5.8.0.txt
|
224
226
|
- doc/release_notes/5.9.0.txt
|
225
227
|
files:
|
@@ -321,6 +323,8 @@ files:
|
|
321
323
|
- doc/release_notes/5.71.0.txt
|
322
324
|
- doc/release_notes/5.72.0.txt
|
323
325
|
- doc/release_notes/5.73.0.txt
|
326
|
+
- doc/release_notes/5.74.0.txt
|
327
|
+
- doc/release_notes/5.75.0.txt
|
324
328
|
- doc/release_notes/5.8.0.txt
|
325
329
|
- doc/release_notes/5.9.0.txt
|
326
330
|
- doc/schema_modification.rdoc
|