sequel 5.55.0 → 5.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +24 -0
- data/README.rdoc +25 -0
- data/doc/cheat_sheet.rdoc +8 -0
- data/doc/opening_databases.rdoc +6 -1
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/derby.rb +5 -0
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/shared/db2.rb +28 -0
- data/lib/sequel/adapters/shared/mssql.rb +34 -0
- data/lib/sequel/adapters/shared/mysql.rb +12 -0
- data/lib/sequel/adapters/shared/oracle.rb +69 -0
- data/lib/sequel/adapters/shared/postgres.rb +60 -3
- data/lib/sequel/adapters/shared/sqlite.rb +6 -0
- data/lib/sequel/database/schema_generator.rb +9 -5
- data/lib/sequel/database/schema_methods.rb +9 -0
- data/lib/sequel/dataset/actions.rb +49 -0
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/query.rb +62 -0
- data/lib/sequel/dataset/sql.rb +112 -25
- data/lib/sequel/extensions/is_distinct_from.rb +139 -0
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_json_ops.rb +2 -1
- data/lib/sequel/extensions/schema_dumper.rb +2 -2
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +13 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f02f6a35677e8af2ea2b164f655cc869213864e5b27f67cdd3e17c7db28c546
|
4
|
+
data.tar.gz: ee09f1b4d5b3f4decbf4d4af3b8ae16ef7e488af40011808dac94f8ca05da89f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89efe5f1a7f2c2eedba5ac1cf336adee1abf489278f79413756e70a41e4ba073ea818562aaf176c1f092e7ab9e763693473a0d96edc6782d9da037c759afd05d
|
7
|
+
data.tar.gz: 84986009ccb30a7a0fd15a555262631f539b95ef9ec8d74d6ca2a4078bd07c33ffda725092bad5afbc93b27901e67dc6d06ca735481448174ac695ef14b6ee99
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 5.58.0 (2022-07-01)
|
2
|
+
|
3
|
+
* Support :disable_split_materialized Database option on MySQL to work around optimizer bug in MariaDB 10.5+ affecting association tests (jeremyevans)
|
4
|
+
|
5
|
+
* Add Dataset#merge* methods to support MERGE statement on PostgreSQL 15+, MSSQL, Oracle, DB2, H2, HSQLDB, and Derby (jeremyevans)
|
6
|
+
|
7
|
+
=== 5.57.0 (2022-06-01)
|
8
|
+
|
9
|
+
* Make Database#create_function on PostgreSQL accept :parallel option (bananarne) (#1870)
|
10
|
+
|
11
|
+
* Add support for :on_update_current_timestamp column option on MySQL (jeremyevans)
|
12
|
+
|
13
|
+
* Add is_distinct_from extension with support for the IS DISTINCT FROM operator (jeremyevans)
|
14
|
+
|
15
|
+
=== 5.56.0 (2022-05-01)
|
16
|
+
|
17
|
+
* Make alter_table add_column/add_foreign_key methods support :index option to create an index on the column (jeremyevans)
|
18
|
+
|
19
|
+
* Support creation of STRICT tables on SQLite 3.37.0+ via create_table :strict option (jeremyevans)
|
20
|
+
|
21
|
+
* Add sqlite_json_ops extension for DSL support for JSON functions and operators added in SQLite 3.38.0 (jeremyevans)
|
22
|
+
|
23
|
+
* Recognize "INTEGER" type same as "integer" type in the schema dumper, helpful on SQLite 3.37.0+ (jeremyevans)
|
24
|
+
|
1
25
|
=== 5.55.0 (2022-04-01)
|
2
26
|
|
3
27
|
* Support :setup_regexp_function Database option in the sqlite adapter to allow the use of regexps when querying (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -414,6 +414,31 @@ As with +delete+, +update+ affects all rows in the dataset, so +where+ first,
|
|
414
414
|
# NOT THIS:
|
415
415
|
posts.update(:state => 'archived').where(Sequel[:stamp] < Date.today - 7)
|
416
416
|
|
417
|
+
=== Merging records
|
418
|
+
|
419
|
+
Merging records using the SQL MERGE statment is done using <tt>merge*</tt> methods.
|
420
|
+
You use +merge_using+ to specify the merge source and join conditions.
|
421
|
+
You can use +merge_insert+, +merge_delete+, and/or +merge_update+ to set the
|
422
|
+
INSERT, DELETE, and UPDATE clauses for the merge. +merge_insert+ takes the same
|
423
|
+
arguments as +insert+, and +merge_update+ takes the same arguments as +update+.
|
424
|
+
+merge_insert+, +merge_delete+, and +merge_update+ can all be called with blocks,
|
425
|
+
to set the conditions for the related INSERT, DELETE, or UPDATE.
|
426
|
+
|
427
|
+
Finally, after calling all of the other <tt>merge_*</tt> methods, you call +merge+
|
428
|
+
to run the MERGE statement on the database.
|
429
|
+
|
430
|
+
ds = DB[:m1]
|
431
|
+
merge_using(:m2, i1: :i2).
|
432
|
+
merge_insert(i1: :i2, a: Sequel[:b]+11).
|
433
|
+
merge_delete{a > 30}.
|
434
|
+
merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
|
435
|
+
|
436
|
+
ds.merge
|
437
|
+
# MERGE INTO m1 USING m2 ON (i1 = i2)
|
438
|
+
# WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
|
439
|
+
# WHEN MATCHED AND (a > 30) THEN DELETE
|
440
|
+
# WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
|
441
|
+
|
417
442
|
=== Transactions
|
418
443
|
|
419
444
|
You can wrap a block of code in a database transaction using the <tt>Database#transaction</tt> method:
|
data/doc/cheat_sheet.rdoc
CHANGED
@@ -57,6 +57,14 @@ Without a filename argument, the sqlite adapter will setup a new sqlite database
|
|
57
57
|
dataset.where{price < 100}.update(:active => true)
|
58
58
|
dataset.where(:active).update(:price => Sequel[:price] * 0.90)
|
59
59
|
|
60
|
+
= Merge rows
|
61
|
+
|
62
|
+
dataset.
|
63
|
+
merge_using(:table, col1: :col2).
|
64
|
+
merge_insert(col3: :col4).
|
65
|
+
merge_delete{col5 > 30}.
|
66
|
+
merge_update(col3: Sequel[:col3] + :col4)
|
67
|
+
|
60
68
|
== Datasets are Enumerable
|
61
69
|
|
62
70
|
dataset.map{|r| r[:name]}
|
data/doc/opening_databases.rdoc
CHANGED
@@ -102,6 +102,7 @@ The following options can be specified and are passed to the database's internal
|
|
102
102
|
:after_connect :: A callable object called after each new connection is made, with the
|
103
103
|
connection object (and server argument if the callable accepts 2 arguments),
|
104
104
|
useful for customizations that you want to apply to all connections (nil by default).
|
105
|
+
:connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
105
106
|
:max_connections :: The maximum size of the connection pool (4 connections by default on most databases)
|
106
107
|
:pool_timeout :: The number of seconds to wait if a connection cannot be acquired before raising an error (5 seconds by default)
|
107
108
|
:single_threaded :: Whether to use a single-threaded (non-thread safe) connection pool
|
@@ -258,6 +259,8 @@ The following additional options are supported:
|
|
258
259
|
:compress :: Whether to compress data sent/received via the socket connection.
|
259
260
|
:config_default_group :: The default group to read from the in the MySQL config file, defaults to "client")
|
260
261
|
:config_local_infile :: If provided, sets the Mysql::OPT_LOCAL_INFILE option on the connection with the given value.
|
262
|
+
:disable_split_materialized :: Set split_materialized=off in the optimizer settings. Necessary to pass the associations
|
263
|
+
integration tests in MariaDB 10.5+, due to a unfixed bug in the optimizer.
|
261
264
|
:encoding :: Specify the encoding/character set to use for the connection.
|
262
265
|
:fractional_seconds :: On MySQL 5.6.5+, this option is recognized and will include fractional seconds in
|
263
266
|
time/timestamp values, as well as have the schema method create columns that can contain
|
@@ -278,7 +281,9 @@ if either the :sslca or :sslkey option is given.
|
|
278
281
|
|
279
282
|
This is a newer MySQL adapter that does typecasting in C, so it is often faster than the
|
280
283
|
mysql adapter. The options given are passed to Mysql2::Client.new, see the mysql2 documentation
|
281
|
-
for details on what options are supported.
|
284
|
+
for details on what options are supported. The :timeout, :auto_is_null, :sql_mode, and :disable_split_materialized
|
285
|
+
options supported by the mysql adapter are also supported for mysql2 adapter (and any other adapters connecting to
|
286
|
+
mysql, such as the jdbc/mysql adapter).
|
282
287
|
|
283
288
|
=== odbc
|
284
289
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* On SQLite, Database#create_table now supports a :strict option to
|
4
|
+
use the STRICT keyword when creating the table. When this option
|
5
|
+
is used, SQLite will enforce the types for each column. When using
|
6
|
+
this option, you are limited to using the following column types:
|
7
|
+
int, integer, real, text, blob, and any (any allows for dynamic
|
8
|
+
types).
|
9
|
+
|
10
|
+
* An sqlite_json_ops extension has been added, providing DSL support
|
11
|
+
for JSON functions and operators supported in SQLite 3.38.0. Usage
|
12
|
+
is similar to the pg_json_ops extension. First, you create an
|
13
|
+
appropriate object:
|
14
|
+
|
15
|
+
j = Sequel.sqlite_json_op(:json_column)
|
16
|
+
# or:
|
17
|
+
j = Sequel[:json_column].sqlite_json_op
|
18
|
+
|
19
|
+
Then, you call methods on that object to create expressions for the
|
20
|
+
JSON functions and operators:
|
21
|
+
|
22
|
+
j[1] # (json_column ->> 1)
|
23
|
+
j.get_text(1) # (json_column -> 1)
|
24
|
+
j.extract('$.a') # json_extract(json_column, '$.a')
|
25
|
+
|
26
|
+
j.array_length # json_array_length(json_column)
|
27
|
+
j.type # json_type(json_column)
|
28
|
+
j.valid # json_valid(json_column)
|
29
|
+
j.json # json(json_column)
|
30
|
+
|
31
|
+
j.insert('$.a', 1) # json_insert(json_column, '$.a', 1)
|
32
|
+
j.set('$.a', 1) # json_set(json_column, '$.a', 1)
|
33
|
+
j.replace('$.a', 1) # json_replace(json_column, '$.a', 1)
|
34
|
+
j.remove('$.a') # json_remove(json_column, '$.a')
|
35
|
+
j.patch('{"a":2}') # json_patch(json_column, '{"a":2}')
|
36
|
+
|
37
|
+
j.each # json_each(json_column)
|
38
|
+
j.tree # json_tree(json_column)
|
39
|
+
|
40
|
+
= Other Improvements
|
41
|
+
|
42
|
+
* The alter_table add_column and add_foreign_key methods now support
|
43
|
+
the :index option to create an index on the added column, for
|
44
|
+
compatibility with the :index option on the create_table column and
|
45
|
+
foreign_key methods.
|
46
|
+
|
47
|
+
* The schema_dumper extension now treats the "INTEGER" type the same
|
48
|
+
as the "integer" type. This fixes some behavior when using SQLite
|
49
|
+
3.37.0+.
|
50
|
+
|
51
|
+
* Sequel's website has a much improved visual design.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An is_distinct_from extension has been added with support for the
|
4
|
+
SQL IS DISTINCT FROM operator. This operator is similar to the
|
5
|
+
not equals operator, except in terms of NULL handling. It returns
|
6
|
+
true if only one side is NULL, and false if both sides are NULL.
|
7
|
+
You can call is_distinct_from on Sequel itself or on Sequel objects:
|
8
|
+
|
9
|
+
Sequel.is_distinct_from(:column_a, :column_b)
|
10
|
+
Sequel[:column_a].is_distinct_from(:column_b)
|
11
|
+
# (column_a IS DISTINCT FROM column_b)
|
12
|
+
|
13
|
+
On databases not supporting IS DISTINCT FROM, support is emulated
|
14
|
+
using a CASE statement.
|
15
|
+
|
16
|
+
* Column definitions on MySQL can use the :on_update_current_timestamp
|
17
|
+
option for ON UPDATE CURRENT_TIMESTAMP, which creates a column that
|
18
|
+
will automatically have its value set to CURRENT_TIMESTAMP on every
|
19
|
+
update.
|
20
|
+
|
21
|
+
* Database#create_function on PostgreSQL now supports a :parallel
|
22
|
+
option to set the thread safety of the funciton. The value should
|
23
|
+
be :safe, :unsafe, or :restricted.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Dataset#merge and related #merge_* methods have been added for the
|
4
|
+
MERGE statement. MERGE is supported on PostgreSQL 15+, Oracle,
|
5
|
+
Microsoft SQL Server, DB2, H2, HSQLDB, and Derby. You can use MERGE
|
6
|
+
to insert, update, and/or delete in a single query. You call
|
7
|
+
the #merge_* methods to setup the MERGE statement, and #merge to
|
8
|
+
execute it on the database:
|
9
|
+
|
10
|
+
ds = DB[:m1]
|
11
|
+
merge_using(:m2, i1: :i2).
|
12
|
+
merge_insert(i1: :i2, a: Sequel[:b]+11).
|
13
|
+
merge_delete{a > 30}.
|
14
|
+
merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
|
15
|
+
|
16
|
+
ds.merge
|
17
|
+
# MERGE INTO m1 USING m2 ON (i1 = i2)
|
18
|
+
# WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
|
19
|
+
# WHEN MATCHED AND (a > 30) THEN DELETE
|
20
|
+
# WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
|
21
|
+
|
22
|
+
On PostgreSQL, the following additional MERGE related methods are
|
23
|
+
available:
|
24
|
+
|
25
|
+
* #merge_do_nothing_when_matched
|
26
|
+
* #merge_do_nothing_when_not_matched
|
27
|
+
|
28
|
+
* A :disable_split_materialized Database option is now supported on
|
29
|
+
MySQL. This disables split_materialized support in the optimizer,
|
30
|
+
working around a bug in MariaDB 10.5+ that causes failures in
|
31
|
+
Sequel's association tests.
|
data/doc/testing.rdoc
CHANGED
@@ -113,7 +113,7 @@ The order in which you delete/truncate the tables is important if you are using
|
|
113
113
|
|
114
114
|
= Testing Sequel Itself
|
115
115
|
|
116
|
-
Sequel has multiple separate test suites. All test suites use minitest/spec, with the minitest-hooks
|
116
|
+
Sequel has multiple separate test suites. All test suites use minitest/spec, with the minitest-hooks and minitest-global_expectations extensions. To install the dependencies necessary to test Sequel, run <tt>gem install --development sequel</tt>.
|
117
117
|
|
118
118
|
== rake
|
119
119
|
|
@@ -179,6 +179,12 @@ module Sequel
|
|
179
179
|
true
|
180
180
|
end
|
181
181
|
|
182
|
+
# HSQLDB 2.3.4+ supports MERGE. Older versions also support MERGE, but not all
|
183
|
+
# features that are in Sequel's tests.
|
184
|
+
def supports_merge?
|
185
|
+
db.db_version >= 20304
|
186
|
+
end
|
187
|
+
|
182
188
|
private
|
183
189
|
|
184
190
|
def empty_from_sql
|
@@ -338,6 +338,11 @@ module Sequel
|
|
338
338
|
true
|
339
339
|
end
|
340
340
|
|
341
|
+
# DB2 supports MERGE
|
342
|
+
def supports_merge?
|
343
|
+
true
|
344
|
+
end
|
345
|
+
|
341
346
|
# DB2 does not support multiple columns in IN.
|
342
347
|
def supports_multiple_column_in?
|
343
348
|
false
|
@@ -360,6 +365,29 @@ module Sequel
|
|
360
365
|
|
361
366
|
private
|
362
367
|
|
368
|
+
# Normalize conditions for MERGE WHEN.
|
369
|
+
def _merge_when_conditions_sql(sql, data)
|
370
|
+
if data.has_key?(:conditions)
|
371
|
+
sql << " AND "
|
372
|
+
literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
|
377
|
+
# type error.
|
378
|
+
def _normalize_merge_when_conditions(conditions)
|
379
|
+
case conditions
|
380
|
+
when nil, false
|
381
|
+
{1=>0}
|
382
|
+
when true
|
383
|
+
{1=>1}
|
384
|
+
when Sequel::SQL::DelayedEvaluation
|
385
|
+
Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
|
386
|
+
else
|
387
|
+
conditions
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
363
391
|
def empty_from_sql
|
364
392
|
' FROM "SYSIBM"."SYSDUMMY1"'
|
365
393
|
end
|
@@ -734,6 +734,11 @@ module Sequel
|
|
734
734
|
false
|
735
735
|
end
|
736
736
|
|
737
|
+
# MSSQL 2008+ supports MERGE
|
738
|
+
def supports_merge?
|
739
|
+
is_2008_or_later?
|
740
|
+
end
|
741
|
+
|
737
742
|
# MSSQL 2005+ supports modifying joined datasets
|
738
743
|
def supports_modifying_joins?
|
739
744
|
is_2005_or_later?
|
@@ -824,6 +829,35 @@ module Sequel
|
|
824
829
|
|
825
830
|
private
|
826
831
|
|
832
|
+
# Normalize conditions for MERGE WHEN.
|
833
|
+
def _merge_when_conditions_sql(sql, data)
|
834
|
+
if data.has_key?(:conditions)
|
835
|
+
sql << " AND "
|
836
|
+
literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
# Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
|
841
|
+
# type error.
|
842
|
+
def _normalize_merge_when_conditions(conditions)
|
843
|
+
case conditions
|
844
|
+
when nil, false
|
845
|
+
{1=>0}
|
846
|
+
when true
|
847
|
+
{1=>1}
|
848
|
+
when Sequel::SQL::DelayedEvaluation
|
849
|
+
Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
|
850
|
+
else
|
851
|
+
conditions
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
# MSSQL requires a semicolon at the end of MERGE.
|
856
|
+
def _merge_when_sql(sql)
|
857
|
+
super
|
858
|
+
sql << ';'
|
859
|
+
end
|
860
|
+
|
827
861
|
# MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
|
828
862
|
def aggregate_dataset
|
829
863
|
(options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
|
@@ -333,6 +333,12 @@ module Sequel
|
|
333
333
|
sqls << "SET sql_mode = '#{sql_mode}'"
|
334
334
|
end
|
335
335
|
|
336
|
+
# Disable the use of split_materialized in the optimizer. This is
|
337
|
+
# needed to pass association tests on MariaDB 10.5+.
|
338
|
+
if opts[:disable_split_materialized] && typecast_value_boolean(opts[:disable_split_materialized])
|
339
|
+
sqls << "SET SESSION optimizer_switch='split_materialized=off'"
|
340
|
+
end
|
341
|
+
|
336
342
|
sqls
|
337
343
|
end
|
338
344
|
|
@@ -356,6 +362,12 @@ module Sequel
|
|
356
362
|
end
|
357
363
|
end
|
358
364
|
|
365
|
+
# Support :on_update_current_timestamp option.
|
366
|
+
def column_definition_default_sql(sql, column)
|
367
|
+
super
|
368
|
+
sql << " ON UPDATE CURRENT_TIMESTAMP" if column[:on_update_current_timestamp]
|
369
|
+
end
|
370
|
+
|
359
371
|
# Add generation clause SQL fragment to column creation SQL.
|
360
372
|
def column_definition_generated_sql(sql, column)
|
361
373
|
if (generated_expression = column[:generated_always_as])
|
@@ -478,6 +478,11 @@ module Sequel
|
|
478
478
|
false
|
479
479
|
end
|
480
480
|
|
481
|
+
# Oracle supports MERGE
|
482
|
+
def supports_merge?
|
483
|
+
true
|
484
|
+
end
|
485
|
+
|
481
486
|
# Oracle supports NOWAIT.
|
482
487
|
def supports_nowait?
|
483
488
|
true
|
@@ -525,6 +530,70 @@ module Sequel
|
|
525
530
|
|
526
531
|
private
|
527
532
|
|
533
|
+
# Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
|
534
|
+
# type error.
|
535
|
+
def _normalize_merge_when_conditions(conditions)
|
536
|
+
case conditions
|
537
|
+
when nil, false
|
538
|
+
{1=>0}
|
539
|
+
when true
|
540
|
+
{1=>1}
|
541
|
+
when Sequel::SQL::DelayedEvaluation
|
542
|
+
Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
|
543
|
+
else
|
544
|
+
conditions
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
# Handle Oracle's non standard MERGE syntax
|
549
|
+
def _merge_when_sql(sql)
|
550
|
+
raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
|
551
|
+
insert = update = delete = nil
|
552
|
+
types = merge_when.map{|d| d[:type]}
|
553
|
+
raise Error, "Oracle does not support multiple INSERT, UPDATE, or DELETE clauses in MERGE" if types != types.uniq
|
554
|
+
|
555
|
+
merge_when.each do |data|
|
556
|
+
case data[:type]
|
557
|
+
when :insert
|
558
|
+
insert = data
|
559
|
+
when :update
|
560
|
+
update = data
|
561
|
+
else # when :delete
|
562
|
+
delete = data
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
if delete
|
567
|
+
raise Error, "Oracle does not support DELETE without UPDATE clause in MERGE" unless update
|
568
|
+
raise Error, "Oracle does not support DELETE without conditions clause in MERGE" unless delete.has_key?(:conditions)
|
569
|
+
end
|
570
|
+
|
571
|
+
if update
|
572
|
+
sql << " WHEN MATCHED"
|
573
|
+
_merge_update_sql(sql, update)
|
574
|
+
_merge_when_conditions_sql(sql, update)
|
575
|
+
|
576
|
+
if delete
|
577
|
+
sql << " DELETE"
|
578
|
+
_merge_when_conditions_sql(sql, delete)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
if insert
|
583
|
+
sql << " WHEN NOT MATCHED"
|
584
|
+
_merge_insert_sql(sql, insert)
|
585
|
+
_merge_when_conditions_sql(sql, insert)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# Handle Oracle's non-standard MERGE WHEN condition syntax.
|
590
|
+
def _merge_when_conditions_sql(sql, data)
|
591
|
+
if data.has_key?(:conditions)
|
592
|
+
sql << " WHERE "
|
593
|
+
literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
528
597
|
# Allow preparing prepared statements, since determining the prepared sql to use for
|
529
598
|
# a prepared statement requires calling prepare on that statement.
|
530
599
|
def allow_preparing_prepared_statements?
|
@@ -414,6 +414,7 @@ module Sequel
|
|
414
414
|
# 2 :: argument name
|
415
415
|
# 3 :: argument mode (e.g. in, out, inout)
|
416
416
|
# :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
|
417
|
+
# :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
|
417
418
|
# :cost :: The estimated cost of the function, used by the query planner.
|
418
419
|
# :language :: The language the function uses. SQL is the default.
|
419
420
|
# :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
|
@@ -1117,6 +1118,7 @@ module Sequel
|
|
1117
1118
|
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
1118
1119
|
#{'STRICT' if opts[:strict]}
|
1119
1120
|
#{'SECURITY DEFINER' if opts[:security_definer]}
|
1121
|
+
#{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
|
1120
1122
|
#{"COST #{opts[:cost]}" if opts[:cost]}
|
1121
1123
|
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
1122
1124
|
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
@@ -1525,7 +1527,7 @@ module Sequel
|
|
1525
1527
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
1526
1528
|
|
1527
1529
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1528
|
-
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
1530
|
+
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']])
|
1529
1531
|
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']])
|
1530
1532
|
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']])
|
1531
1533
|
|
@@ -1758,6 +1760,41 @@ module Sequel
|
|
1758
1760
|
nil
|
1759
1761
|
end
|
1760
1762
|
|
1763
|
+
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
1764
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1765
|
+
# use it as additional conditions for the match.
|
1766
|
+
#
|
1767
|
+
# merge_do_nothing_when_matched
|
1768
|
+
# # WHEN MATCHED THEN DO NOTHING
|
1769
|
+
#
|
1770
|
+
# merge_do_nothing_when_matched{a > 30}
|
1771
|
+
# # WHEN MATCHED AND (a > 30) THEN DO NOTHING
|
1772
|
+
def merge_do_nothing_when_matched(&block)
|
1773
|
+
_merge_when(:type=>:matched, &block)
|
1774
|
+
end
|
1775
|
+
|
1776
|
+
# Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
|
1777
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1778
|
+
# use it as additional conditions for the match.
|
1779
|
+
#
|
1780
|
+
# merge_do_nothing_when_not_matched
|
1781
|
+
# # WHEN NOT MATCHED THEN DO NOTHING
|
1782
|
+
#
|
1783
|
+
# merge_do_nothing_when_not_matched{a > 30}
|
1784
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
|
1785
|
+
def merge_do_nothing_when_not_matched(&block)
|
1786
|
+
_merge_when(:type=>:not_matched, &block)
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
1790
|
+
def merge_insert(*values, &block)
|
1791
|
+
h = {:type=>:insert, :values=>values}
|
1792
|
+
if override = @opts[:override]
|
1793
|
+
h[:override] = insert_override_sql(String.new)
|
1794
|
+
end
|
1795
|
+
_merge_when(h, &block)
|
1796
|
+
end
|
1797
|
+
|
1761
1798
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
1762
1799
|
# always use the user supplied value, and an error is not raised for identity
|
1763
1800
|
# columns that are GENERATED ALWAYS.
|
@@ -1825,6 +1862,11 @@ module Sequel
|
|
1825
1862
|
true
|
1826
1863
|
end
|
1827
1864
|
|
1865
|
+
# PostgreSQL 15+ supports MERGE.
|
1866
|
+
def supports_merge?
|
1867
|
+
server_version >= 150000
|
1868
|
+
end
|
1869
|
+
|
1828
1870
|
# PostgreSQL supports NOWAIT.
|
1829
1871
|
def supports_nowait?
|
1830
1872
|
true
|
@@ -1935,6 +1977,22 @@ module Sequel
|
|
1935
1977
|
|
1936
1978
|
private
|
1937
1979
|
|
1980
|
+
# Append the INSERT sql used in a MERGE
|
1981
|
+
def _merge_insert_sql(sql, data)
|
1982
|
+
sql << " THEN INSERT "
|
1983
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
1984
|
+
_insert_columns_sql(sql, columns)
|
1985
|
+
if override = data[:override]
|
1986
|
+
sql << override
|
1987
|
+
end
|
1988
|
+
_insert_values_sql(sql, values)
|
1989
|
+
end
|
1990
|
+
|
1991
|
+
def _merge_matched_sql(sql, data)
|
1992
|
+
sql << " THEN DO NOTHING"
|
1993
|
+
end
|
1994
|
+
alias _merge_not_matched_sql _merge_matched_sql
|
1995
|
+
|
1938
1996
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
1939
1997
|
def _truncate_sql(table)
|
1940
1998
|
to = @opts[:truncate_opts] || OPTS
|
@@ -2011,14 +2069,13 @@ module Sequel
|
|
2011
2069
|
end
|
2012
2070
|
|
2013
2071
|
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
2014
|
-
def
|
2072
|
+
def insert_override_sql(sql)
|
2015
2073
|
case opts[:override]
|
2016
2074
|
when :system
|
2017
2075
|
sql << " OVERRIDING SYSTEM VALUE"
|
2018
2076
|
when :user
|
2019
2077
|
sql << " OVERRIDING USER VALUE"
|
2020
2078
|
end
|
2021
|
-
super
|
2022
2079
|
end
|
2023
2080
|
|
2024
2081
|
# For multiple table support, PostgreSQL requires at least
|
@@ -337,6 +337,11 @@ module Sequel
|
|
337
337
|
ps
|
338
338
|
end
|
339
339
|
|
340
|
+
# Support creating STRICT tables via :strict option
|
341
|
+
def create_table_sql(name, generator, options)
|
342
|
+
"#{super}#{' STRICT' if options[:strict]}"
|
343
|
+
end
|
344
|
+
|
340
345
|
# SQLite support creating temporary views.
|
341
346
|
def create_view_prefix_sql(name, options)
|
342
347
|
create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
|
@@ -347,6 +352,7 @@ module Sequel
|
|
347
352
|
/foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
|
348
353
|
/\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
|
349
354
|
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
355
|
+
/\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
|
350
356
|
/may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
|
351
357
|
/\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
|
352
358
|
}.freeze
|
@@ -146,6 +146,9 @@ module Sequel
|
|
146
146
|
#
|
147
147
|
# :generated_type :: Set the type of column when using :generated_always_as,
|
148
148
|
# should be :virtual or :stored to force a type.
|
149
|
+
# :on_update_current_timestamp :: Use ON UPDATE CURRENT TIMESTAMP when defining the column,
|
150
|
+
# which will update the column value to CURRENT_TIMESTAMP
|
151
|
+
# on every UPDATE.
|
149
152
|
#
|
150
153
|
# Microsoft SQL Server specific options:
|
151
154
|
#
|
@@ -387,8 +390,7 @@ module Sequel
|
|
387
390
|
end
|
388
391
|
|
389
392
|
# Add a column with the given name, type, and opts.
|
390
|
-
# See CreateTableGenerator#column for the available options
|
391
|
-
# separate +add_index+ call to add an index for the column).
|
393
|
+
# See CreateTableGenerator#column for the available options.
|
392
394
|
#
|
393
395
|
# add_column(:name, String) # ADD COLUMN name varchar(255)
|
394
396
|
#
|
@@ -401,7 +403,10 @@ module Sequel
|
|
401
403
|
# :after :: The name of an existing column that the new column should be positioned after
|
402
404
|
# :first :: Create this new column before all other existing columns
|
403
405
|
def add_column(name, type, opts = OPTS)
|
404
|
-
|
406
|
+
op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
|
407
|
+
index_opts = op.delete(:index)
|
408
|
+
@operations << op
|
409
|
+
add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
|
405
410
|
nil
|
406
411
|
end
|
407
412
|
|
@@ -430,8 +435,7 @@ module Sequel
|
|
430
435
|
end
|
431
436
|
|
432
437
|
# Add a foreign key with the given name and referencing the given table.
|
433
|
-
# See CreateTableGenerator#column for the available options
|
434
|
-
# separate +add_index+ call to add an index for the column).
|
438
|
+
# See CreateTableGenerator#column for the available options.
|
435
439
|
#
|
436
440
|
# You can also pass an array of column names for creating composite foreign
|
437
441
|
# keys. In this case, it will assume the columns exist and will only add
|