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