sequel 5.77.0 → 5.79.0

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