sequel 5.77.0 → 5.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9951772dd3785c8ce91090bd31034a1e57ac5dcbc17af774b980a115cea15347
4
- data.tar.gz: 3cfcaefaf6aa4758ef2cfaf78cbe6ccdc688e8b3836718bd63c42c5bdb220026
3
+ metadata.gz: 154517820668c8c59f141797157a9945ae3afaeb482e997619e68e9bebc5c70a
4
+ data.tar.gz: c7991308e696d0f931f46cf38c301986c9094a44bca94a1d6749a0b798cb1d9c
5
5
  SHA512:
6
- metadata.gz: '0885577c11fd7285a5278d0a1b52ae411e557cec7bce2f3a49f8128a0be63928ebfa55b2b1a86c8b28ee1c3782c1dc5ebf5d4ecf00f0194ad33da89cb6ffc12a'
7
- data.tar.gz: ccc7b8b6c68049ee1fed2211a3d193157ee36fde9c340324eba2a8cebd8925bc97b4dc1777b61f8a2fb94d83439c41f33afb832c080f4d54bab16d09924a4fe2
6
+ metadata.gz: 7efb6286d8eded4c0064199f7335e9cd2b0575942fd52040b29c52e26498d1531168435d86ca6e42474512ac21c1b151db85dd4db0fa5ff6b3f90dad73568a3b
7
+ data.tar.gz: 7ef17f79333fdabe90485b105fdc24925c8c75f41dc78f83f5400ebc39b0ac2b3709881624086526559132c8346c1dc8f07b9384a39bb838d1132bf4a7040ec1
data/CHANGELOG CHANGED
@@ -1,3 +1,21 @@
1
+ === 5.79.0 (2024-04-01)
2
+
3
+ * Support create_or_replace_view with :materialized option on PostgreSQL (nashby) (#2144)
4
+
5
+ * Support :unlogged_tables_default Database option on Postgres for making created tables unlogged by default (jeremyevans) (#2134)
6
+
7
+ * Add Dataset#select_prepend for prepending to the current selected columns (jeremyevans) (#2139)
8
+
9
+ === 5.78.0 (2024-03-01)
10
+
11
+ * Support SQLite 3.45+ jsonb functions in the sqlite_json_ops extension (jeremyevans) (#2133)
12
+
13
+ * Support compounds (e.g. UNION) in conjunction with Database#values on PostgreSQL (jeremyevans) (#2137)
14
+
15
+ * Support :use_advisory_lock option to Migrator.run to use advisory locks when running migrations (jeremyevans) (#2089)
16
+
17
+ * Support Database#with_advisory_lock on PostgreSQL, MySQL, and Microsoft SQL Server (jeremyevans) (#2089)
18
+
1
19
  === 5.77.0 (2024-02-01)
2
20
 
3
21
  * Support create_table :without_rowid option on SQLite (loranger32) (#2126)
@@ -16,7 +34,7 @@
16
34
 
17
35
  * Support on_duplicate_columns={raise,warn} parameter in connection URL when using duplicate_columns_handler extension (jeremyevans)
18
36
 
19
- * Add transaction_connection_validator extension for retrying transactions on new connection if ther is a disconnect error when starting transaction (jeremyevans)
37
+ * Add transaction_connection_validator extension for retrying transactions on new connection if there is a disconnect error when starting transaction (jeremyevans)
20
38
 
21
39
  === 5.76.0 (2024-01-01)
22
40
 
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
- Alternate Discussion Forum (sequel-talk Google Group) :: http://groups.google.com/group/sequel-talk
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 or the sequel-talk Google Group.
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 is an +order_append+ equivalent for +select+ called +select_append+:
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
 
@@ -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
@@ -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 add to the existing selected columns, use +select_append+:
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).
@@ -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 third argument is an options hash, and when using the +column+
85
- method, the fourth argument is the options hash. The following options are supported:
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 named. For unnamed arguments, this should be an
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)
@@ -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[:unlogged]
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
@@ -149,6 +149,7 @@ module Sequel
149
149
  @schemas = {}
150
150
  @prepared_statements = {}
151
151
  @transactions = {}
152
+ @transactions.compare_by_identity
152
153
  @symbol_literal_cache = {}
153
154
 
154
155
  @timezone = nil
@@ -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
@@ -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
@@ -944,14 +944,8 @@ module Sequel
944
944
  # DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
945
945
  # DB[:items].select_append(:b) # SELECT *, b FROM items
946
946
  def select_append(*columns, &block)
947
- cur_sel = @opts[:select]
948
- if !cur_sel || cur_sel.empty?
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)
947
+ virtual_row_columns(columns, block)
948
+ select(*(_current_select(true) + columns))
955
949
  end
956
950
 
957
951
  # Set both the select and group clauses with the given +columns+.
@@ -973,6 +967,18 @@ module Sequel
973
967
  select_append(*columns, &block)
974
968
  end
975
969
 
970
+ # Returns a copy of the dataset with the given columns added
971
+ # to the existing selected columns. If no columns are currently selected,
972
+ # it will select the columns given in addition to *.
973
+ #
974
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
975
+ # DB[:items].select(:a).select_prepend(:b) # SELECT b, a FROM items
976
+ # DB[:items].select_prepend(:b) # SELECT b, * FROM items
977
+ def select_prepend(*columns, &block)
978
+ virtual_row_columns(columns, block)
979
+ select(*(columns + _current_select(false)))
980
+ end
981
+
976
982
  # Set the server for this dataset to use. Used to pick a specific database
977
983
  # shard to run a query against, or to override the default (where SELECT uses
978
984
  # :read_only database and all other queries use the :default database). This
@@ -1353,6 +1359,36 @@ module Sequel
1353
1359
  end
1354
1360
  # :nocov:
1355
1361
 
1362
+ # A frozen array for the currently selected columns.
1363
+ def _current_select(allow_plain_wildcard)
1364
+ cur_sel = @opts[:select]
1365
+
1366
+ if !cur_sel || cur_sel.empty?
1367
+ cur_sel = if allow_plain_wildcard && supports_select_all_and_column?
1368
+ [WILDCARD].freeze
1369
+ else
1370
+ _current_select_column_all
1371
+ end
1372
+ elsif !allow_plain_wildcard && cur_sel.include?(WILDCARD)
1373
+ cur_sel = cur_sel.dup
1374
+ index = cur_sel.index(WILDCARD)
1375
+ cur_sel.delete(WILDCARD)
1376
+ _current_select_column_all.each_with_index do |ca, i|
1377
+ cur_sel.insert(index+i, ca)
1378
+ end
1379
+ cur_sel.freeze
1380
+ end
1381
+
1382
+ cur_sel
1383
+ end
1384
+
1385
+ # An array of SQL::ColumnAll objects for all FROM and JOIN tables. Used for select_append
1386
+ # and select_prepend.
1387
+ def _current_select_column_all
1388
+ tables = Array(@opts[:from]) + Array(@opts[:join])
1389
+ tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze
1390
+ end
1391
+
1356
1392
  # If invert is true, invert the condition.
1357
1393
  def _invert_filter(cond, invert)
1358
1394
  if invert
@@ -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
 
@@ -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
- migrator_class(directory).new(db, directory, opts).run
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
@@ -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 objects,
12
- # which you can create via Sequel.sqlite_json_op:
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 method
17
- # to create a Sequel::SQLite::JSONOp object:
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
- # j.json # json(json_column)
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
- # The JSONOp class is a simple container for a single object that
54
- # defines methods that yield Sequel expression objects representing
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 JSONOp < Sequel::SQL::Wrapper
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, function(:array_length, *args))
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
- function(:each, *args)
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
- self.class.new(SQL::Function.new(:json, self))
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
- function(:tree, *args)
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, function(:type, *args))
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, function(:valid))
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("json_#{name}", self, *args)
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
@@ -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
@@ -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 = 77
9
+ MINOR = 79
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.77.0
4
+ version: 5.79.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-02-01 00:00:00.000000000 Z
11
+ date: 2024-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -224,6 +224,8 @@ 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
228
230
  - doc/release_notes/5.9.0.txt
229
231
  files:
@@ -329,6 +331,8 @@ files:
329
331
  - doc/release_notes/5.75.0.txt
330
332
  - doc/release_notes/5.76.0.txt
331
333
  - doc/release_notes/5.77.0.txt
334
+ - doc/release_notes/5.78.0.txt
335
+ - doc/release_notes/5.79.0.txt
332
336
  - doc/release_notes/5.8.0.txt
333
337
  - doc/release_notes/5.9.0.txt
334
338
  - doc/schema_modification.rdoc