sequel 5.77.0 → 5.80.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 +27 -1
- data/README.rdoc +5 -3
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/opening_databases.rdoc +2 -0
- data/doc/querying.rdoc +6 -1
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/release_notes/5.79.0.txt +28 -0
- data/doc/release_notes/5.80.0.txt +40 -0
- data/doc/schema_modification.rdoc +2 -2
- data/lib/sequel/adapters/shared/mssql.rb +29 -1
- data/lib/sequel/adapters/shared/mysql.rb +37 -2
- data/lib/sequel/adapters/shared/postgres.rb +37 -2
- data/lib/sequel/database/misc.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +2 -2
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/query.rb +58 -9
- data/lib/sequel/exceptions.rb +5 -0
- data/lib/sequel/extensions/async_thread_pool.rb +7 -0
- data/lib/sequel/extensions/caller_logging.rb +2 -1
- data/lib/sequel/extensions/migration.rb +12 -1
- data/lib/sequel/extensions/provenance.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/model/base.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddf0e8e9009f39ca7f6dffca8aef10ef4fbdf22a8c5002def5ecea4042f38763
|
4
|
+
data.tar.gz: b9f3cb97273dd9885e8002a64e17bddd0a6db2f932d1c68ce97844e2ac843096
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7387d403f32cb492abe6fad35d6a87a4ac274fc3602aee0f8afba38d9feb30578ee774de2b168a88d86ab740a41fa2a62333f695c0d2fa507fde775943917daa
|
7
|
+
data.tar.gz: 11b1c1e9daf92153585f158358ee85078b83decfca8bcf4569fa90e59335dfd123a0d8fddf8f6e58e166801029de23937279dc3aa746d0d3878a34dac90ce636
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
=== 5.80.0 (2024-05-01)
|
2
|
+
|
3
|
+
* Support Dataset#skip_locked on MariaDB 10.6+ (simi) (#2150)
|
4
|
+
|
5
|
+
* Avoid allocating datasets in cases where the returned dataset would be the same as the receiver (jeremyevans)
|
6
|
+
|
7
|
+
* Add provenance dataset extension, which includes comments in queries showing how and where the dataset was built (jeremyevans)
|
8
|
+
|
9
|
+
=== 5.79.0 (2024-04-01)
|
10
|
+
|
11
|
+
* Support create_or_replace_view with :materialized option on PostgreSQL (nashby) (#2144)
|
12
|
+
|
13
|
+
* Support :unlogged_tables_default Database option on Postgres for making created tables unlogged by default (jeremyevans) (#2134)
|
14
|
+
|
15
|
+
* Add Dataset#select_prepend for prepending to the current selected columns (jeremyevans) (#2139)
|
16
|
+
|
17
|
+
=== 5.78.0 (2024-03-01)
|
18
|
+
|
19
|
+
* Support SQLite 3.45+ jsonb functions in the sqlite_json_ops extension (jeremyevans) (#2133)
|
20
|
+
|
21
|
+
* Support compounds (e.g. UNION) in conjunction with Database#values on PostgreSQL (jeremyevans) (#2137)
|
22
|
+
|
23
|
+
* Support :use_advisory_lock option to Migrator.run to use advisory locks when running migrations (jeremyevans) (#2089)
|
24
|
+
|
25
|
+
* Support Database#with_advisory_lock on PostgreSQL, MySQL, and Microsoft SQL Server (jeremyevans) (#2089)
|
26
|
+
|
1
27
|
=== 5.77.0 (2024-02-01)
|
2
28
|
|
3
29
|
* Support create_table :without_rowid option on SQLite (loranger32) (#2126)
|
@@ -16,7 +42,7 @@
|
|
16
42
|
|
17
43
|
* Support on_duplicate_columns={raise,warn} parameter in connection URL when using duplicate_columns_handler extension (jeremyevans)
|
18
44
|
|
19
|
-
* Add transaction_connection_validator extension for retrying transactions on new connection if
|
45
|
+
* Add transaction_connection_validator extension for retrying transactions on new connection if there is a disconnect error when starting transaction (jeremyevans)
|
20
46
|
|
21
47
|
=== 5.76.0 (2024-01-01)
|
22
48
|
|
data/README.rdoc
CHANGED
@@ -22,10 +22,10 @@ RDoc Documentation :: https://sequel.jeremyevans.net/rdoc
|
|
22
22
|
Source Code :: https://github.com/jeremyevans/sequel
|
23
23
|
Bug tracking (GitHub Issues) :: https://github.com/jeremyevans/sequel/issues
|
24
24
|
Discussion Forum (GitHub Discussions) :: https://github.com/jeremyevans/sequel/discussions
|
25
|
-
|
25
|
+
Archived Discussion Forum (sequel-talk Google Group) :: https://www.mail-archive.com/sequel-talk@googlegroups.com/
|
26
26
|
|
27
27
|
If you have questions about how to use Sequel, please ask on
|
28
|
-
GitHub Discussions
|
28
|
+
GitHub Discussions.
|
29
29
|
Only use the the bug tracker to report
|
30
30
|
bugs in Sequel, not to ask for help on using Sequel.
|
31
31
|
|
@@ -368,10 +368,12 @@ Like +order+, +select+ overrides an existing selection:
|
|
368
368
|
posts.select(:stamp).select(:name)
|
369
369
|
# SELECT name FROM posts
|
370
370
|
|
371
|
-
As you might expect, there
|
371
|
+
As you might expect, there are +order_append+ and +order_prepend+ equivalents for +select+ called +select_append+ and +select_prepend+:
|
372
372
|
|
373
373
|
posts.select(:stamp).select_append(:name)
|
374
374
|
# SELECT stamp, name FROM posts
|
375
|
+
posts.select(:stamp).select_prepend(:name)
|
376
|
+
# SELECT name, stamp FROM posts
|
375
377
|
|
376
378
|
=== Deleting Records
|
377
379
|
|
data/doc/dataset_basics.rdoc
CHANGED
@@ -65,7 +65,7 @@ Most Dataset methods that users will use can be broken down into two types:
|
|
65
65
|
|
66
66
|
Most dataset methods fall into this category, which can be further broken down by the clause they affect:
|
67
67
|
|
68
|
-
SELECT:: select, select_all, select_append, select_group, select_more
|
68
|
+
SELECT:: select, select_all, select_append, select_group, select_more, select_prepend
|
69
69
|
FROM:: from, from_self
|
70
70
|
JOIN:: join, left_join, right_join, full_join, natural_join, natural_left_join, natural_right_join, natural_full_join, cross_join, inner_join, left_outer_join, right_outer_join, full_outer_join, join_table
|
71
71
|
WHERE:: where, filter, exclude, or, grep, invert, unfiltered
|
data/doc/opening_databases.rdoc
CHANGED
@@ -346,6 +346,8 @@ The following additional options are supported:
|
|
346
346
|
separated by commas (for use via a URL: <tt>postgres:///?search_path=schema1,schema2</tt>), or it
|
347
347
|
can be an array of strings (for use via an option:
|
348
348
|
<tt>Sequel.postgres(search_path: ['schema1', 'schema2'])</tt>).
|
349
|
+
:unlogged_tables_default :: Set to true to use UNLOGGED by default for created tables, for potentially better performance
|
350
|
+
when data integrity is not important.
|
349
351
|
:use_iso_date_format :: This can be set to false to not force the ISO date format. Sequel forces
|
350
352
|
it by default to allow for an optimization.
|
351
353
|
|
data/doc/querying.rdoc
CHANGED
@@ -624,11 +624,16 @@ Like +order+, +select+ replaces the existing selected columns:
|
|
624
624
|
Artist.select(:id).select(:name)
|
625
625
|
# SELECT name FROM artists
|
626
626
|
|
627
|
-
To
|
627
|
+
To append to the existing selected columns, use +select_append+:
|
628
628
|
|
629
629
|
Artist.select(:id).select_append(:name)
|
630
630
|
# SELECT id, name FROM artists
|
631
631
|
|
632
|
+
To prepend to the existing selected columns, use +select_prepend+:
|
633
|
+
|
634
|
+
Artist.select(:id).select_prepend(:name)
|
635
|
+
# SELECT name, id FROM artists
|
636
|
+
|
632
637
|
To remove specifically selected columns, and default back to all
|
633
638
|
columns, use +select_all+:
|
634
639
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* SQLite 3.45+ jsonb functions are now supported in the sqlite_json_ops
|
4
|
+
extension. Similar to the postgres_json_ops extension, there are
|
5
|
+
now separate methods for dealing with json and jsonb types:
|
6
|
+
|
7
|
+
Sequel.sqlite_json_op(:column) # json
|
8
|
+
Sequel.sqlite_jsonb_op(:column) # jsonb
|
9
|
+
|
10
|
+
Some methods that use json_* functions for json ops use jsonb_*
|
11
|
+
functions for jsonb ops:
|
12
|
+
|
13
|
+
jb = Sequel.sqlite_jsonb_op(:column)
|
14
|
+
jb.extract('$.a') # jsonb_extract(column, '$.a')
|
15
|
+
jb.insert('$.a', 1) # jsonb_insert(column, '$.a', 1)
|
16
|
+
jb.set('$.a', 1) # jsonb_set(column, '$.a', 1)
|
17
|
+
jb.replace('$.a', 1) # jsonb_replace(column, '$.a', 1)
|
18
|
+
jb.remove('$.a') # jsonb_remove(column, '$.a')
|
19
|
+
jb.patch('{"a":2}') # jsonb_patch(column, '{"a":2}')
|
20
|
+
|
21
|
+
You can use the json and jsonb methods to convert jsonb to json
|
22
|
+
and json to jsonb, respectively.
|
23
|
+
|
24
|
+
jb.json # json(column)
|
25
|
+
|
26
|
+
Use of the json method on jsonb types is important, because if you
|
27
|
+
want to be able to deal with the values in Ruby, you must convert
|
28
|
+
the jsonb value to json in the database before the database returns
|
29
|
+
the value. Unlike PostgreSQL, SQLite will not convert the value
|
30
|
+
from jsonb to json on retrieval, and direct use of SQLite's jsonb
|
31
|
+
format is unsupported by SQLite as it is subject to change.
|
32
|
+
|
33
|
+
* Database#with_advisory_lock is now supported on PostgreSQL, MySQL,
|
34
|
+
and Microsoft SQL Server. This supports advisory (explicit)
|
35
|
+
locking, using the database-specific APIs. To work on all three
|
36
|
+
servers, lock ids should be integers in the signed 64-bit range.
|
37
|
+
|
38
|
+
DB.with_advisory_lock(1234) do
|
39
|
+
# do something
|
40
|
+
end
|
41
|
+
|
42
|
+
By default, an AdvisoryLockError is raised if the lock cannot be
|
43
|
+
immediately acquired. You can use the :wait option to wait until
|
44
|
+
the lock can be acquired, instead of raising.
|
45
|
+
|
46
|
+
DB.with_advisory_lock(1234, wait: true) do
|
47
|
+
# do something
|
48
|
+
end
|
49
|
+
|
50
|
+
* Migrator.run now supports a :use_advisory_lock option to use
|
51
|
+
advisory locks when running migrations, so that it does not
|
52
|
+
attempt to run the same migration more than once in the case
|
53
|
+
where multiple processes are running the migrator simultaneously.
|
54
|
+
It's probably best to avoid running the migrator in multiple
|
55
|
+
processes simultaneously instead of relying on this option.
|
56
|
+
|
57
|
+
= Other Improvements
|
58
|
+
|
59
|
+
* Database#values now supports chaining with compounds on
|
60
|
+
PostgreSQL.
|
61
|
+
|
62
|
+
DB.values([[1, 2]]).union(DB.values([[3, 4]]))
|
63
|
+
# SELECT * FROM (VALUES (1, 2) UNION (VALUES (3, 4))) AS t1
|
64
|
+
|
65
|
+
* The internal hash used to store transaction metadata now uses
|
66
|
+
compare_by_identity, which is faster and avoids potential
|
67
|
+
issues if a driver implements connection object equality.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Dataset#select_prepend has been added for prepending to the
|
4
|
+
currently selected columns:
|
5
|
+
|
6
|
+
DB[:table].select_prepend(:column)
|
7
|
+
# SELECT column, table.*
|
8
|
+
|
9
|
+
As not all databases support "SELECT column, *", select_prepend
|
10
|
+
qualifies wildcard selections to all tables referenced in the
|
11
|
+
query.
|
12
|
+
|
13
|
+
The only reason to use select_prepend is if you want the hashes
|
14
|
+
returned by Sequel to be in a specific order. Otherwise, it is
|
15
|
+
better to use select_append.
|
16
|
+
|
17
|
+
* On PostgreSQL, Sequel now supports an :unlogged_tables_default
|
18
|
+
Database option, which will default created tables to be UNLOGGED.
|
19
|
+
This can be useful to speedup testing in some cases, but it should
|
20
|
+
never be used in cases where data integrity is important.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* On PostgreSQL, Database#create_or_replace_view now supports the
|
25
|
+
:materialized option. This allows for dropping an existing
|
26
|
+
materialized view and creating a new one with the same name
|
27
|
+
(PostgreSQL does not have native support for replacing materialized
|
28
|
+
views).
|
@@ -0,0 +1,40 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A provenance dataset extension has been added. This extension makes
|
4
|
+
SQL queries include a comment describing how the dataset was built.
|
5
|
+
This can make debugging complex cases significantly easier. Here's
|
6
|
+
a simple example:
|
7
|
+
|
8
|
+
DB.extension :provenance
|
9
|
+
|
10
|
+
DB[:table].
|
11
|
+
select(:a).
|
12
|
+
where{b > 10}.
|
13
|
+
order(:c).
|
14
|
+
limit(10)
|
15
|
+
# SQL:
|
16
|
+
# SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
|
17
|
+
# -- Dataset Provenance
|
18
|
+
# -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
|
19
|
+
# -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
|
20
|
+
# -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
|
21
|
+
# -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
|
22
|
+
# -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
|
23
|
+
|
24
|
+
With the above example, it's obvious how the dataset is created, but
|
25
|
+
but in real applications, where datasets can be built from multiple
|
26
|
+
files, seeing where each dataset clone was made can be helpful.
|
27
|
+
|
28
|
+
The source listed will skip locations in the Ruby standard library
|
29
|
+
as well as Sequel itself. Other locations can be skipped by
|
30
|
+
providing a Database :provenance_caller_ignore Regexp option:
|
31
|
+
|
32
|
+
DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
|
33
|
+
|
34
|
+
= Other Improvements
|
35
|
+
|
36
|
+
* For dataset methods where Sequel can determine that the return
|
37
|
+
value would be equivalent to the receiver, Sequel now returns the
|
38
|
+
receiver. This reduces the number of dataset allocations.
|
39
|
+
|
40
|
+
* Sequel now supports Dataset#skip_locked on MariaDB 10.6+.
|
@@ -81,8 +81,8 @@ appropriate 64-bit integer type for the database you are using.
|
|
81
81
|
|
82
82
|
=== Column options
|
83
83
|
|
84
|
-
When using the type name as method, the
|
85
|
-
method, the
|
84
|
+
When using the type name as method, the second argument is an options hash, and when using the +column+
|
85
|
+
method, the third argument is the options hash. The following options are supported:
|
86
86
|
|
87
87
|
:default :: The default value for the column.
|
88
88
|
:index :: Create an index on this column. If given a hash, use the hash as the
|
@@ -32,7 +32,7 @@ module Sequel
|
|
32
32
|
#
|
33
33
|
# Options:
|
34
34
|
# :args :: Arguments to stored procedure. For named arguments, this should be a
|
35
|
-
# hash keyed by argument
|
35
|
+
# hash keyed by argument name. For unnamed arguments, this should be an
|
36
36
|
# array. Output parameters to the function are specified using :output.
|
37
37
|
# You can also name output parameters and provide a type by using an
|
38
38
|
# array containing :output, the type name, and the parameter name.
|
@@ -246,6 +246,34 @@ module Sequel
|
|
246
246
|
information_schema_tables('VIEW', opts)
|
247
247
|
end
|
248
248
|
|
249
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will
|
250
|
+
# be converted to a string). If successful, yield to the block, then release the advisory lock
|
251
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
252
|
+
#
|
253
|
+
# Options:
|
254
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
255
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
256
|
+
lock_id = lock_id.to_s
|
257
|
+
timeout = opts[:wait] ? -1 : 0
|
258
|
+
server = opts[:server]
|
259
|
+
|
260
|
+
synchronize(server) do
|
261
|
+
begin
|
262
|
+
res = call_mssql_sproc(:sp_getapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockTimeout'=>timeout, 'LockMode'=>'Exclusive', 'LockOwner'=>'Session'})
|
263
|
+
|
264
|
+
unless locked = res[:result] >= 0
|
265
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
266
|
+
end
|
267
|
+
|
268
|
+
yield
|
269
|
+
ensure
|
270
|
+
if locked
|
271
|
+
call_mssql_sproc(:sp_releaseapplock, :server=>server, :args=>{'Resource'=>lock_id, 'LockOwner'=>'Session'})
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
249
277
|
private
|
250
278
|
|
251
279
|
# Add CLUSTERED or NONCLUSTERED as needed
|
@@ -197,6 +197,41 @@ module Sequel
|
|
197
197
|
renames.each{|from,| remove_cached_schema(from)}
|
198
198
|
end
|
199
199
|
|
200
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which will be
|
201
|
+
# converted to a string). If successful, yield to the block, then release the advisory lock
|
202
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
203
|
+
#
|
204
|
+
# DB.with_advisory_lock(1357){DB.get(1)}
|
205
|
+
# # SELECT GET_LOCK('1357', 0) LIMIT 1
|
206
|
+
# # SELECT 1 AS v LIMIT 1
|
207
|
+
# # SELECT RELEASE_LOCK('1357') LIMIT 1
|
208
|
+
#
|
209
|
+
# Options:
|
210
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
211
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
212
|
+
lock_id = lock_id.to_s
|
213
|
+
ds = dataset
|
214
|
+
if server = opts[:server]
|
215
|
+
ds = ds.server(server)
|
216
|
+
end
|
217
|
+
|
218
|
+
# MariaDB doesn't support negative values for infinite wait. A wait of 34 years
|
219
|
+
# should be reasonably similar to infinity for this case.
|
220
|
+
timeout = opts[:wait] ? 1073741823 : 0
|
221
|
+
|
222
|
+
synchronize(server) do |c|
|
223
|
+
begin
|
224
|
+
unless locked = ds.get{GET_LOCK(lock_id, timeout)} == 1
|
225
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
226
|
+
end
|
227
|
+
|
228
|
+
yield
|
229
|
+
ensure
|
230
|
+
ds.get{RELEASE_LOCK(lock_id)} if locked
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
200
235
|
private
|
201
236
|
|
202
237
|
def alter_table_add_column_sql(table, op)
|
@@ -891,9 +926,9 @@ module Sequel
|
|
891
926
|
(type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
|
892
927
|
end
|
893
928
|
|
894
|
-
# MySQL 8+
|
929
|
+
# MySQL 8+ and MariaDB 10.6+ support SKIP LOCKED.
|
895
930
|
def supports_skip_locked?
|
896
|
-
|
931
|
+
db.server_version >= (db.mariadb? ? 100600 : 80000)
|
897
932
|
end
|
898
933
|
|
899
934
|
# Check the database setting for whether fractional timestamps
|
@@ -859,6 +859,41 @@ module Sequel
|
|
859
859
|
pg_class_relname(relkind, opts)
|
860
860
|
end
|
861
861
|
|
862
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which should be
|
863
|
+
# a 64-bit integer). If successful, yield to the block, then release the advisory lock
|
864
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
865
|
+
#
|
866
|
+
# DB.with_advisory_lock(1347){DB.get(1)}
|
867
|
+
# # SELECT pg_try_advisory_lock(1357) LIMIT 1
|
868
|
+
# # SELECT 1 AS v LIMIT 1
|
869
|
+
# # SELECT pg_advisory_unlock(1357) LIMIT 1
|
870
|
+
#
|
871
|
+
# Options:
|
872
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
873
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
874
|
+
ds = dataset
|
875
|
+
if server = opts[:server]
|
876
|
+
ds = ds.server(server)
|
877
|
+
end
|
878
|
+
|
879
|
+
synchronize(server) do |c|
|
880
|
+
begin
|
881
|
+
if opts[:wait]
|
882
|
+
ds.get{pg_advisory_lock(lock_id)}
|
883
|
+
locked = true
|
884
|
+
else
|
885
|
+
unless locked = ds.get{pg_try_advisory_lock(lock_id)}
|
886
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
yield
|
891
|
+
ensure
|
892
|
+
ds.get{pg_advisory_unlock(lock_id)} if locked
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
862
897
|
private
|
863
898
|
|
864
899
|
# Dataset used to retrieve CHECK constraint information
|
@@ -1390,7 +1425,7 @@ module Sequel
|
|
1390
1425
|
elsif options[:foreign]
|
1391
1426
|
raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
|
1392
1427
|
'FOREIGN '
|
1393
|
-
elsif options[:
|
1428
|
+
elsif options.fetch(:unlogged){typecast_value_boolean(@opts[:unlogged_tables_default])}
|
1394
1429
|
'UNLOGGED '
|
1395
1430
|
end
|
1396
1431
|
|
@@ -1785,7 +1820,7 @@ module Sequel
|
|
1785
1820
|
|
1786
1821
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1787
1822
|
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
1788
|
-
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1823
|
+
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values compounds order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1789
1824
|
Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
|
1790
1825
|
|
1791
1826
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -252,10 +252,10 @@ module Sequel
|
|
252
252
|
# For databases where replacing a view is not natively supported, support
|
253
253
|
# is emulated by dropping a view with the same name before creating the view.
|
254
254
|
def create_or_replace_view(name, source, options = OPTS)
|
255
|
-
if supports_create_or_replace_view?
|
255
|
+
if supports_create_or_replace_view? && !options[:materialized]
|
256
256
|
options = options.merge(:replace=>true)
|
257
257
|
else
|
258
|
-
swallow_database_error{drop_view(name)}
|
258
|
+
swallow_database_error{drop_view(name, options)}
|
259
259
|
end
|
260
260
|
|
261
261
|
create_view(name, source, options)
|
@@ -21,7 +21,7 @@ module Sequel
|
|
21
21
|
where exclude exclude_having having
|
22
22
|
distinct grep group group_and_count group_append
|
23
23
|
limit offset order order_append order_prepend reverse
|
24
|
-
select select_all select_append select_group server
|
24
|
+
select select_all select_append select_group select_prepend server
|
25
25
|
METHS
|
26
26
|
|
27
27
|
# Define a method in the module
|
data/lib/sequel/dataset/graph.rb
CHANGED
data/lib/sequel/dataset/query.rb
CHANGED
@@ -43,7 +43,7 @@ module Sequel
|
|
43
43
|
add_graph_aliases distinct except exclude exclude_having
|
44
44
|
filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
|
45
45
|
limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
|
46
|
-
reverse reverse_order select select_all select_append select_group select_more server
|
46
|
+
reverse reverse_order select select_all select_append select_group select_more select_prepend server
|
47
47
|
set_graph_aliases unfiltered ungraphed ungrouped union
|
48
48
|
unlimited unordered where with with_recursive with_sql
|
49
49
|
METHS
|
@@ -129,6 +129,7 @@ module Sequel
|
|
129
129
|
def distinct(*args, &block)
|
130
130
|
virtual_row_columns(args, block)
|
131
131
|
if args.empty?
|
132
|
+
return self if opts[:distinct] == EMPTY_ARRAY
|
132
133
|
cached_dataset(:_distinct_ds){clone(:distinct => EMPTY_ARRAY)}
|
133
134
|
else
|
134
135
|
raise(InvalidOperation, "DISTINCT ON not supported") unless supports_distinct_on?
|
@@ -230,6 +231,7 @@ module Sequel
|
|
230
231
|
#
|
231
232
|
# DB[:table].for_update # SELECT * FROM table FOR UPDATE
|
232
233
|
def for_update
|
234
|
+
return self if opts[:lock] == :update
|
233
235
|
cached_dataset(:_for_update_ds){lock_style(:update)}
|
234
236
|
end
|
235
237
|
|
@@ -641,6 +643,7 @@ module Sequel
|
|
641
643
|
# DB.from(:a, DB[:b].where(Sequel[:a][:c]=>Sequel[:b][:d]).lateral)
|
642
644
|
# # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
|
643
645
|
def lateral
|
646
|
+
return self if opts[:lateral]
|
644
647
|
cached_dataset(:_lateral_ds){clone(:lateral=>true)}
|
645
648
|
end
|
646
649
|
|
@@ -744,6 +747,7 @@ module Sequel
|
|
744
747
|
# ds.all # => [{2=>:id}]
|
745
748
|
# ds.naked.all # => [{:id=>2}]
|
746
749
|
def naked
|
750
|
+
return self unless opts[:row_proc]
|
747
751
|
cached_dataset(:_naked_ds){with_row_proc(nil)}
|
748
752
|
end
|
749
753
|
|
@@ -753,6 +757,7 @@ module Sequel
|
|
753
757
|
# DB[:items].for_update.nowait
|
754
758
|
# # SELECT * FROM items FOR UPDATE NOWAIT
|
755
759
|
def nowait
|
760
|
+
return self if opts[:nowait]
|
756
761
|
cached_dataset(:_nowait_ds) do
|
757
762
|
raise(Error, 'This dataset does not support raises errors instead of waiting for locked rows') unless supports_nowait?
|
758
763
|
clone(:nowait=>true)
|
@@ -878,6 +883,7 @@ module Sequel
|
|
878
883
|
# end
|
879
884
|
def returning(*values)
|
880
885
|
if values.empty?
|
886
|
+
return self if opts[:returning] == EMPTY_ARRAY
|
881
887
|
cached_dataset(:_returning_ds) do
|
882
888
|
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
883
889
|
clone(:returning=>EMPTY_ARRAY)
|
@@ -930,6 +936,7 @@ module Sequel
|
|
930
936
|
# DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
|
931
937
|
def select_all(*tables)
|
932
938
|
if tables.empty?
|
939
|
+
return self unless opts[:select]
|
933
940
|
cached_dataset(:_select_all_ds){clone(:select => nil)}
|
934
941
|
else
|
935
942
|
select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
|
@@ -944,14 +951,8 @@ module Sequel
|
|
944
951
|
# DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
|
945
952
|
# DB[:items].select_append(:b) # SELECT *, b FROM items
|
946
953
|
def select_append(*columns, &block)
|
947
|
-
|
948
|
-
|
949
|
-
unless supports_select_all_and_column?
|
950
|
-
return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
|
951
|
-
end
|
952
|
-
cur_sel = [WILDCARD]
|
953
|
-
end
|
954
|
-
select(*(cur_sel + columns), &block)
|
954
|
+
virtual_row_columns(columns, block)
|
955
|
+
select(*(_current_select(true) + columns))
|
955
956
|
end
|
956
957
|
|
957
958
|
# Set both the select and group clauses with the given +columns+.
|
@@ -973,6 +974,18 @@ module Sequel
|
|
973
974
|
select_append(*columns, &block)
|
974
975
|
end
|
975
976
|
|
977
|
+
# Returns a copy of the dataset with the given columns added
|
978
|
+
# to the existing selected columns. If no columns are currently selected,
|
979
|
+
# it will select the columns given in addition to *.
|
980
|
+
#
|
981
|
+
# DB[:items].select(:a).select(:b) # SELECT b FROM items
|
982
|
+
# DB[:items].select(:a).select_prepend(:b) # SELECT b, a FROM items
|
983
|
+
# DB[:items].select_prepend(:b) # SELECT b, * FROM items
|
984
|
+
def select_prepend(*columns, &block)
|
985
|
+
virtual_row_columns(columns, block)
|
986
|
+
select(*(columns + _current_select(false)))
|
987
|
+
end
|
988
|
+
|
976
989
|
# Set the server for this dataset to use. Used to pick a specific database
|
977
990
|
# shard to run a query against, or to override the default (where SELECT uses
|
978
991
|
# :read_only database and all other queries use the :default database). This
|
@@ -999,6 +1012,7 @@ module Sequel
|
|
999
1012
|
|
1000
1013
|
# Specify that the check for limits/offsets when updating/deleting be skipped for the dataset.
|
1001
1014
|
def skip_limit_check
|
1015
|
+
return self if opts[:skip_limit_check]
|
1002
1016
|
cached_dataset(:_skip_limit_check_ds) do
|
1003
1017
|
clone(:skip_limit_check=>true)
|
1004
1018
|
end
|
@@ -1006,6 +1020,7 @@ module Sequel
|
|
1006
1020
|
|
1007
1021
|
# Skip locked rows when returning results from this dataset.
|
1008
1022
|
def skip_locked
|
1023
|
+
return self if opts[:skip_locked]
|
1009
1024
|
cached_dataset(:_skip_locked_ds) do
|
1010
1025
|
raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
|
1011
1026
|
clone(:skip_locked=>true)
|
@@ -1017,6 +1032,7 @@ module Sequel
|
|
1017
1032
|
# DB[:items].group(:a).having(a: 1).where(:b).unfiltered
|
1018
1033
|
# # SELECT * FROM items GROUP BY a
|
1019
1034
|
def unfiltered
|
1035
|
+
return self unless opts[:where] || opts[:having]
|
1020
1036
|
cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
|
1021
1037
|
end
|
1022
1038
|
|
@@ -1025,6 +1041,7 @@ module Sequel
|
|
1025
1041
|
# DB[:items].group(:a).having(a: 1).where(:b).ungrouped
|
1026
1042
|
# # SELECT * FROM items WHERE b
|
1027
1043
|
def ungrouped
|
1044
|
+
return self unless opts[:group] || opts[:having]
|
1028
1045
|
cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
|
1029
1046
|
end
|
1030
1047
|
|
@@ -1052,6 +1069,7 @@ module Sequel
|
|
1052
1069
|
#
|
1053
1070
|
# DB[:items].limit(10, 20).unlimited # SELECT * FROM items
|
1054
1071
|
def unlimited
|
1072
|
+
return self unless opts[:limit] || opts[:offset]
|
1055
1073
|
cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
|
1056
1074
|
end
|
1057
1075
|
|
@@ -1059,6 +1077,7 @@ module Sequel
|
|
1059
1077
|
#
|
1060
1078
|
# DB[:items].order(:a).unordered # SELECT * FROM items
|
1061
1079
|
def unordered
|
1080
|
+
return self unless opts[:order]
|
1062
1081
|
cached_dataset(:_unordered_ds){clone(:order=>nil)}
|
1063
1082
|
end
|
1064
1083
|
|
@@ -1353,6 +1372,36 @@ module Sequel
|
|
1353
1372
|
end
|
1354
1373
|
# :nocov:
|
1355
1374
|
|
1375
|
+
# A frozen array for the currently selected columns.
|
1376
|
+
def _current_select(allow_plain_wildcard)
|
1377
|
+
cur_sel = @opts[:select]
|
1378
|
+
|
1379
|
+
if !cur_sel || cur_sel.empty?
|
1380
|
+
cur_sel = if allow_plain_wildcard && supports_select_all_and_column?
|
1381
|
+
[WILDCARD].freeze
|
1382
|
+
else
|
1383
|
+
_current_select_column_all
|
1384
|
+
end
|
1385
|
+
elsif !allow_plain_wildcard && cur_sel.include?(WILDCARD)
|
1386
|
+
cur_sel = cur_sel.dup
|
1387
|
+
index = cur_sel.index(WILDCARD)
|
1388
|
+
cur_sel.delete(WILDCARD)
|
1389
|
+
_current_select_column_all.each_with_index do |ca, i|
|
1390
|
+
cur_sel.insert(index+i, ca)
|
1391
|
+
end
|
1392
|
+
cur_sel.freeze
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
cur_sel
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
# An array of SQL::ColumnAll objects for all FROM and JOIN tables. Used for select_append
|
1399
|
+
# and select_prepend.
|
1400
|
+
def _current_select_column_all
|
1401
|
+
tables = Array(@opts[:from]) + Array(@opts[:join])
|
1402
|
+
tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze
|
1403
|
+
end
|
1404
|
+
|
1356
1405
|
# If invert is true, invert the condition.
|
1357
1406
|
def _invert_filter(cond, invert)
|
1358
1407
|
if invert
|
data/lib/sequel/exceptions.rb
CHANGED
@@ -18,6 +18,11 @@ module Sequel
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
(
|
23
|
+
# Error raised when there is a failed attempt to acquire an advisory lock.
|
24
|
+
AdvisoryLockError = Class.new(Error)
|
25
|
+
).name
|
21
26
|
|
22
27
|
(
|
23
28
|
# Error raised when the adapter requested doesn't exist or can't be loaded.
|
@@ -176,6 +176,13 @@
|
|
176
176
|
# +:preempt_async_thread+ Database option before loading the
|
177
177
|
# async_thread_pool extension.
|
178
178
|
#
|
179
|
+
# Note that the async_thread_pool extension creates the thread pool
|
180
|
+
# when it is loaded into the Database. If you fork after loading
|
181
|
+
# the extension, the extension will not work, as fork does not
|
182
|
+
# copy the thread pools. If you are using a forking webserver
|
183
|
+
# (or any other system that forks worker processes), load this
|
184
|
+
# extension in each child process, do not load it before forking.
|
185
|
+
#
|
179
186
|
# Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
|
180
187
|
|
181
188
|
|
@@ -36,6 +36,7 @@ require 'rbconfig'
|
|
36
36
|
module Sequel
|
37
37
|
module CallerLogging
|
38
38
|
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
39
|
+
RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
|
39
40
|
|
40
41
|
# A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
|
41
42
|
attr_accessor :caller_logging_ignore
|
@@ -59,7 +60,7 @@ module Sequel
|
|
59
60
|
ignore = caller_logging_ignore
|
60
61
|
c = caller.find do |line|
|
61
62
|
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
62
|
-
line.start_with?(
|
63
|
+
line.start_with?(RUBY_STDLIB) ||
|
63
64
|
(ignore && line =~ ignore))
|
64
65
|
end
|
65
66
|
|
@@ -403,6 +403,11 @@ module Sequel
|
|
403
403
|
migrator_class(directory).new(db, directory, opts).is_current?
|
404
404
|
end
|
405
405
|
|
406
|
+
# Lock ID to use for advisory locks when running migrations
|
407
|
+
# "sequel-migration".codepoints.reduce(:*) % (2**63)
|
408
|
+
MIGRATION_ADVISORY_LOCK_ID = 4966325471869609408
|
409
|
+
private_constant :MIGRATION_ADVISORY_LOCK_ID
|
410
|
+
|
406
411
|
# Migrates the supplied database using the migration files in the specified directory. Options:
|
407
412
|
# :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
|
408
413
|
# It is very risky to use this option, since it can result in
|
@@ -416,6 +421,8 @@ module Sequel
|
|
416
421
|
# :table :: The table containing the schema version (default: :schema_info for integer migrations and
|
417
422
|
# :schema_migrations for timestamped migrations).
|
418
423
|
# :target :: The target version to which to migrate. If not given, migrates to the maximum version.
|
424
|
+
# :use_advisory_lock :: Use advisory locks in migrations (only use this if Sequel supports advisory
|
425
|
+
# locks for the database).
|
419
426
|
#
|
420
427
|
# Examples:
|
421
428
|
# Sequel::Migrator.run(DB, "migrations")
|
@@ -423,7 +430,11 @@ module Sequel
|
|
423
430
|
# Sequel::Migrator.run(DB, "app1/migrations", column: :app2_version)
|
424
431
|
# Sequel::Migrator.run(DB, "app2/migrations", column: :app2_version, table: :schema_info2)
|
425
432
|
def self.run(db, directory, opts=OPTS)
|
426
|
-
|
433
|
+
if opts[:use_advisory_lock]
|
434
|
+
db.with_advisory_lock(MIGRATION_ADVISORY_LOCK_ID){run(db, directory, opts.merge(:use_advisory_lock=>false))}
|
435
|
+
else
|
436
|
+
migrator_class(directory).new(db, directory, opts).run
|
437
|
+
end
|
427
438
|
end
|
428
439
|
|
429
440
|
# Choose the Migrator subclass to use. Uses the TimestampMigrator
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The provenance dataset extension tracks the locations of all
|
4
|
+
# dataset clones that resulted in the current dataset, and includes
|
5
|
+
# the information as a comment in the dataset's SQL. This makes it
|
6
|
+
# possible to see how a query was built, which can aid debugging.
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# DB[:table].
|
10
|
+
# select(:a).
|
11
|
+
# where{b > 10}.
|
12
|
+
# order(:c).
|
13
|
+
# limit(10)
|
14
|
+
# # SQL:
|
15
|
+
# # SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
|
16
|
+
# # -- Dataset Provenance
|
17
|
+
# # -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
|
18
|
+
# # -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
|
19
|
+
# # -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
|
20
|
+
# # -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
|
21
|
+
# # -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
|
22
|
+
#
|
23
|
+
# With the above example, the source is fairly obvious and not helpful,
|
24
|
+
# but in real applications, where datasets can be built from multiple
|
25
|
+
# files, seeing where each dataset clone was made can be helpful.
|
26
|
+
#
|
27
|
+
# The Source listed will skip locations in the Ruby standard library
|
28
|
+
# as well as Sequel itself. Other locations can be skipped by
|
29
|
+
# providing a Database :provenance_caller_ignore Regexp option:
|
30
|
+
#
|
31
|
+
# DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
|
32
|
+
#
|
33
|
+
# Related module: Sequel::Dataset::Provenance
|
34
|
+
|
35
|
+
#
|
36
|
+
module Sequel
|
37
|
+
class Dataset
|
38
|
+
module Provenance
|
39
|
+
SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
|
40
|
+
RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
|
41
|
+
|
42
|
+
if TRUE_FREEZE
|
43
|
+
# Include provenance information when cloning datasets.
|
44
|
+
def clone(opts = nil || (return self))
|
45
|
+
super(provenance_opts(opts))
|
46
|
+
end
|
47
|
+
else
|
48
|
+
# :nocov:
|
49
|
+
def clone(opts = OPTS) # :nodoc:
|
50
|
+
super(provenance_opts(opts))
|
51
|
+
end
|
52
|
+
# :nocov:
|
53
|
+
end
|
54
|
+
|
55
|
+
%w'select insert update delete'.each do |type|
|
56
|
+
# Include the provenance information as a comment when preparing dataset SQL
|
57
|
+
define_method(:"#{type}_sql") do |*a|
|
58
|
+
sql = super(*a)
|
59
|
+
|
60
|
+
if provenance = @opts[:provenance]
|
61
|
+
comment = provenance.map do |hash|
|
62
|
+
" -- Keys:#{hash[:keys].inspect} Source:#{hash[:source]}".to_s.gsub(/\s+/, ' ')
|
63
|
+
end
|
64
|
+
comment << ""
|
65
|
+
comment.unshift " -- Dataset Provenance"
|
66
|
+
comment.unshift " -- "
|
67
|
+
comment = comment.join("\n")
|
68
|
+
|
69
|
+
if sql.frozen?
|
70
|
+
sql += comment
|
71
|
+
sql.freeze
|
72
|
+
elsif @opts[:append_sql] || @opts[:placeholder_literalizer]
|
73
|
+
sql << comment
|
74
|
+
else
|
75
|
+
sql += comment
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
sql
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Return a copy of opts with provenance information added.
|
86
|
+
def provenance_opts(opts)
|
87
|
+
provenance = {source: provenance_source, keys: opts.keys.freeze}.freeze
|
88
|
+
opts = opts.dup
|
89
|
+
opts[:provenance] = ((@opts[:provenance] || EMPTY_ARRAY).dup << provenance).freeze
|
90
|
+
opts
|
91
|
+
end
|
92
|
+
|
93
|
+
# Return the caller line for the provenance change. This skips
|
94
|
+
# Sequel itself and the standard library. Additional locations
|
95
|
+
# can be skipped using the :provenance_caller_ignore Dataset option.
|
96
|
+
def provenance_source
|
97
|
+
ignore = db.opts[:provenance_caller_ignore]
|
98
|
+
caller.find do |line|
|
99
|
+
!(line.start_with?(SEQUEL_LIB_PATH) ||
|
100
|
+
line.start_with?(RUBY_STDLIB) ||
|
101
|
+
(ignore && line =~ ignore))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
register_extension(:provenance, Provenance)
|
107
|
+
end
|
108
|
+
end
|
@@ -2,27 +2,34 @@
|
|
2
2
|
#
|
3
3
|
# The sqlite_json_ops extension adds support to Sequel's DSL to make
|
4
4
|
# it easier to call SQLite JSON functions and operators (added
|
5
|
-
# first in SQLite 3.38.0).
|
5
|
+
# first in SQLite 3.38.0). It also supports the SQLite JSONB functions
|
6
|
+
# added in SQLite 3.45.0.
|
6
7
|
#
|
7
8
|
# To load the extension:
|
8
9
|
#
|
9
10
|
# Sequel.extension :sqlite_json_ops
|
10
11
|
#
|
11
|
-
# This extension works by calling methods on Sequel::SQLite::JSONOp
|
12
|
-
# which you can create
|
12
|
+
# This extension works by calling methods on Sequel::SQLite::JSONOp and
|
13
|
+
# Sequel::SQLite::JSONBOp objects, which you can create using
|
14
|
+
# Sequel.sqlite_json_op and Sequel.sqlite_jsonb_op:
|
13
15
|
#
|
14
16
|
# j = Sequel.sqlite_json_op(:json_column)
|
17
|
+
# jb = Sequel.sqlite_jsonb_op(:jsonb_column)
|
15
18
|
#
|
16
|
-
# Also, on most Sequel expression objects, you can call the sqlite_json_op
|
17
|
-
# to create a Sequel::SQLite::JSONOp
|
19
|
+
# Also, on most Sequel expression objects, you can call the sqlite_json_op or
|
20
|
+
# sqlite_jsonb_op method to create a Sequel::SQLite::JSONOp or
|
21
|
+
# Sequel::SQLite::JSONBOp object:
|
18
22
|
#
|
19
23
|
# j = Sequel[:json_column].sqlite_json_op
|
24
|
+
# jb = Sequel[:jsonb_column].sqlite_jsonb_op
|
20
25
|
#
|
21
26
|
# If you have loaded the {core_extensions extension}[rdoc-ref:doc/core_extensions.rdoc],
|
22
27
|
# or you have loaded the core_refinements extension
|
23
28
|
# and have activated refinements for the file, you can also use Symbol#sqlite_json_op:
|
29
|
+
# or Symbol#sqlite_jsonb_op:
|
24
30
|
#
|
25
31
|
# j = :json_column.sqlite_json_op
|
32
|
+
# jb = :json_column.sqlite_jsonb_op
|
26
33
|
#
|
27
34
|
# The following methods are available for Sequel::SQLite::JSONOp instances:
|
28
35
|
#
|
@@ -30,11 +37,13 @@
|
|
30
37
|
# j.get(1) # (json_column ->> 1)
|
31
38
|
# j.get_text(1) # (json_column -> 1)
|
32
39
|
# j.extract('$.a') # json_extract(json_column, '$.a')
|
40
|
+
# jb.extract('$.a') # jsonb_extract(jsonb_column, '$.a')
|
33
41
|
#
|
34
42
|
# j.array_length # json_array_length(json_column)
|
35
43
|
# j.type # json_type(json_column)
|
36
44
|
# j.valid # json_valid(json_column)
|
37
|
-
#
|
45
|
+
# jb.json # json(jsonb_column)
|
46
|
+
# j.jsonb # jsonb(json_column)
|
38
47
|
#
|
39
48
|
# j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
|
40
49
|
# j.set('$.a', 1) # json_set(json_column, '$.a', 1)
|
@@ -42,22 +51,30 @@
|
|
42
51
|
# j.remove('$.a') # json_remove(json_column, '$.a')
|
43
52
|
# j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
|
44
53
|
#
|
54
|
+
# jb.insert('$.a', 1) # jsonb_insert(jsonb_column, '$.a', 1)
|
55
|
+
# jb.set('$.a', 1) # jsonb_set(jsonb_column, '$.a', 1)
|
56
|
+
# jb.replace('$.a', 1) # jsonb_replace(jsonb_column, '$.a', 1)
|
57
|
+
# jb.remove('$.a') # jsonb_remove(jsonb_column, '$.a')
|
58
|
+
# jb.patch('{"a":2}') # jsonb_patch(jsonb_column, '{"a":2}')
|
59
|
+
#
|
45
60
|
# j.each # json_each(json_column)
|
46
61
|
# j.tree # json_tree(json_column)
|
47
62
|
#
|
48
|
-
# Related modules: Sequel::SQLite::JSONOp
|
63
|
+
# Related modules: Sequel::SQLite::JSONBaseOp, Sequel::SQLite::JSONOp,
|
64
|
+
# Sequel::SQLite::JSONBOp
|
49
65
|
|
50
66
|
#
|
51
67
|
module Sequel
|
52
68
|
module SQLite
|
53
|
-
#
|
54
|
-
# defines methods that
|
55
|
-
# SQLite json operators and functions.
|
69
|
+
# JSONBaseOp is an abstract base wrapper class for a object that
|
70
|
+
# defines methods that return Sequel expression objects representing
|
71
|
+
# SQLite json operators and functions. It is subclassed by both
|
72
|
+
# JSONOp and JSONBOp for json and jsonb specific behavior.
|
56
73
|
#
|
57
74
|
# In the method documentation examples, assume that:
|
58
75
|
#
|
59
76
|
# json_op = Sequel.sqlite_json_op(:json)
|
60
|
-
class
|
77
|
+
class JSONBaseOp < Sequel::SQL::Wrapper
|
61
78
|
GET = ["(".freeze, " ->> ".freeze, ")".freeze].freeze
|
62
79
|
private_constant :GET
|
63
80
|
|
@@ -82,7 +99,7 @@ module Sequel
|
|
82
99
|
# json_op.array_length # json_array_length(json)
|
83
100
|
# json_op.array_length('$[1]') # json_array_length(json, '$[1]')
|
84
101
|
def array_length(*args)
|
85
|
-
Sequel::SQL::NumericExpression.new(:NOOP,
|
102
|
+
Sequel::SQL::NumericExpression.new(:NOOP, SQL::Function.new(:json_array_length, self, *args))
|
86
103
|
end
|
87
104
|
|
88
105
|
# Returns an expression for a set of information extracted from the top-level
|
@@ -92,7 +109,7 @@ module Sequel
|
|
92
109
|
# json_op.each # json_each(json)
|
93
110
|
# json_op.each('$.a') # json_each(json, '$.a')
|
94
111
|
def each(*args)
|
95
|
-
|
112
|
+
SQL::Function.new(:json_each, self, *args)
|
96
113
|
end
|
97
114
|
|
98
115
|
# Returns an expression for the JSON array element or object field at the specified
|
@@ -129,10 +146,17 @@ module Sequel
|
|
129
146
|
#
|
130
147
|
# json_op.json # json(json)
|
131
148
|
def json
|
132
|
-
|
149
|
+
JSONOp.new(SQL::Function.new(:json, self))
|
133
150
|
end
|
134
151
|
alias minify json
|
135
152
|
|
153
|
+
# Returns the JSONB format of the JSON.
|
154
|
+
#
|
155
|
+
# json_op.jsonb # jsonb(json)
|
156
|
+
def jsonb
|
157
|
+
JSONBOp.new(SQL::Function.new(:jsonb, self))
|
158
|
+
end
|
159
|
+
|
136
160
|
# Returns an expression for updating the JSON object using the RFC 7396 MergePatch algorithm
|
137
161
|
#
|
138
162
|
# json_op.patch('{"a": 1, "b": null}') # json_patch(json, '{"a": 1, "b": null}')
|
@@ -172,7 +196,7 @@ module Sequel
|
|
172
196
|
# json_op.tree # json_tree(json)
|
173
197
|
# json_op.tree('$.a') # json_tree(json, '$.a')
|
174
198
|
def tree(*args)
|
175
|
-
|
199
|
+
SQL::Function.new(:json_tree, self, *args)
|
176
200
|
end
|
177
201
|
|
178
202
|
# Returns an expression for the type of the JSON value or the JSON value at the given path.
|
@@ -180,13 +204,13 @@ module Sequel
|
|
180
204
|
# json_op.type # json_type(json)
|
181
205
|
# json_op.type('$[1]') # json_type(json, '$[1]')
|
182
206
|
def type(*args)
|
183
|
-
Sequel::SQL::StringExpression.new(:NOOP,
|
207
|
+
Sequel::SQL::StringExpression.new(:NOOP, SQL::Function.new(:json_type, self, *args))
|
184
208
|
end
|
185
209
|
alias typeof type
|
186
210
|
|
187
211
|
# Returns a boolean expression for whether the JSON is valid or not.
|
188
212
|
def valid
|
189
|
-
Sequel::SQL::BooleanExpression.new(:NOOP,
|
213
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, SQL::Function.new(:json_valid, self))
|
190
214
|
end
|
191
215
|
|
192
216
|
private
|
@@ -198,7 +222,7 @@ module Sequel
|
|
198
222
|
|
199
223
|
# Internals of the methods that return functions prefixed with +json_+.
|
200
224
|
def function(name, *args)
|
201
|
-
SQL::Function.new("
|
225
|
+
SQL::Function.new("#{function_prefix}_#{name}", self, *args)
|
202
226
|
end
|
203
227
|
|
204
228
|
# Internals of the methods that return functions prefixed with +json_+, that
|
@@ -208,12 +232,36 @@ module Sequel
|
|
208
232
|
end
|
209
233
|
end
|
210
234
|
|
235
|
+
# JSONOp is used for SQLite json-specific functions and operators.
|
236
|
+
class JSONOp < JSONBaseOp
|
237
|
+
private
|
238
|
+
|
239
|
+
def function_prefix
|
240
|
+
"json"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# JSONOp is used for SQLite jsonb-specific functions and operators.
|
245
|
+
class JSONBOp < JSONBaseOp
|
246
|
+
private
|
247
|
+
|
248
|
+
def function_prefix
|
249
|
+
"jsonb"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
211
253
|
module JSONOpMethods
|
212
254
|
# Wrap the receiver in an JSONOp so you can easily use the SQLite
|
213
255
|
# json functions and operators with it.
|
214
256
|
def sqlite_json_op
|
215
257
|
JSONOp.new(self)
|
216
258
|
end
|
259
|
+
|
260
|
+
# Wrap the receiver in an JSONBOp so you can easily use the SQLite
|
261
|
+
# jsonb functions and operators with it.
|
262
|
+
def sqlite_jsonb_op
|
263
|
+
JSONBOp.new(self)
|
264
|
+
end
|
217
265
|
end
|
218
266
|
end
|
219
267
|
|
@@ -227,6 +275,16 @@ module Sequel
|
|
227
275
|
SQLite::JSONOp.new(v)
|
228
276
|
end
|
229
277
|
end
|
278
|
+
|
279
|
+
# Return the object wrapped in an SQLite::JSONBOp.
|
280
|
+
def sqlite_jsonb_op(v)
|
281
|
+
case v
|
282
|
+
when SQLite::JSONBOp
|
283
|
+
v
|
284
|
+
else
|
285
|
+
SQLite::JSONBOp.new(v)
|
286
|
+
end
|
287
|
+
end
|
230
288
|
end
|
231
289
|
|
232
290
|
class SQL::GenericExpression
|
data/lib/sequel/model/base.rb
CHANGED
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
# natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
|
18
18
|
# order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
|
19
19
|
# right_outer_join, select, select_all, select_append, select_group, select_hash,
|
20
|
-
# select_hash_groups, select_map, select_more, select_order_map, server,
|
20
|
+
# select_hash_groups, select_map, select_more, select_order_map, select_prepend, server,
|
21
21
|
# single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
|
22
22
|
# truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
|
23
23
|
# where_each, where_single_value, with, with_recursive, with_sql
|
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 = 80
|
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.80.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: 2024-
|
11
|
+
date: 2024-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -224,7 +224,10 @@ extra_rdoc_files:
|
|
224
224
|
- doc/release_notes/5.75.0.txt
|
225
225
|
- doc/release_notes/5.76.0.txt
|
226
226
|
- doc/release_notes/5.77.0.txt
|
227
|
+
- doc/release_notes/5.78.0.txt
|
228
|
+
- doc/release_notes/5.79.0.txt
|
227
229
|
- doc/release_notes/5.8.0.txt
|
230
|
+
- doc/release_notes/5.80.0.txt
|
228
231
|
- doc/release_notes/5.9.0.txt
|
229
232
|
files:
|
230
233
|
- CHANGELOG
|
@@ -329,7 +332,10 @@ files:
|
|
329
332
|
- doc/release_notes/5.75.0.txt
|
330
333
|
- doc/release_notes/5.76.0.txt
|
331
334
|
- doc/release_notes/5.77.0.txt
|
335
|
+
- doc/release_notes/5.78.0.txt
|
336
|
+
- doc/release_notes/5.79.0.txt
|
332
337
|
- doc/release_notes/5.8.0.txt
|
338
|
+
- doc/release_notes/5.80.0.txt
|
333
339
|
- doc/release_notes/5.9.0.txt
|
334
340
|
- doc/schema_modification.rdoc
|
335
341
|
- doc/security.rdoc
|
@@ -492,6 +498,7 @@ files:
|
|
492
498
|
- lib/sequel/extensions/pg_static_cache_updater.rb
|
493
499
|
- lib/sequel/extensions/pg_timestamptz.rb
|
494
500
|
- lib/sequel/extensions/pretty_table.rb
|
501
|
+
- lib/sequel/extensions/provenance.rb
|
495
502
|
- lib/sequel/extensions/query.rb
|
496
503
|
- lib/sequel/extensions/round_timestamps.rb
|
497
504
|
- lib/sequel/extensions/run_transaction_hooks.rb
|
@@ -662,7 +669,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
662
669
|
- !ruby/object:Gem::Version
|
663
670
|
version: '0'
|
664
671
|
requirements: []
|
665
|
-
rubygems_version: 3.5.
|
672
|
+
rubygems_version: 3.5.9
|
666
673
|
signing_key:
|
667
674
|
specification_version: 4
|
668
675
|
summary: The Database Toolkit for Ruby
|