sequel 5.77.0 → 5.80.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|