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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfed01eed8d575d13963be4568c8935f82c47a59b2be32d382439d50153f032d
4
- data.tar.gz: 9ac182c00cdc1f54c26626005f990c5e0a05eebaae3b3c5b88bac0fcd770cd8e
3
+ metadata.gz: 2f02f6a35677e8af2ea2b164f655cc869213864e5b27f67cdd3e17c7db28c546
4
+ data.tar.gz: ee09f1b4d5b3f4decbf4d4af3b8ae16ef7e488af40011808dac94f8ca05da89f
5
5
  SHA512:
6
- metadata.gz: c9aceee8528a749c541f74eeb07610b520e4342ac8d11abda3222fd0d760e2ff2a16e1b08b7931f5d24cb0ae9fced020ef695798ffac6c4b4af15508e8927468
7
- data.tar.gz: 19e40f6386373bdbba07b5a65faf5ffc99a5693e81b0eaeaf1abffc9b07cc29c2688ea658629f633eb36a3427f74857cab28216d18cfb208cca97a75ea10788e
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]}
@@ -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, minitest-global_expectations, and minitest-shared_description extensions. To install the dependencies necessary to test Sequel, run <tt>gem install --development sequel</tt>.
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
 
@@ -235,6 +235,11 @@ module Sequel
235
235
  false
236
236
  end
237
237
 
238
+ # Derby 10.11+ supports MERGE.
239
+ def supports_merge?
240
+ db.svn_version >= 1616546
241
+ end
242
+
238
243
  # Derby does not support IN/NOT IN with multiple columns
239
244
  def supports_multiple_column_in?
240
245
  false
@@ -229,6 +229,11 @@ module Sequel
229
229
  false
230
230
  end
231
231
 
232
+ # H2 supports MERGE
233
+ def supports_merge?
234
+ true
235
+ end
236
+
232
237
  # H2 doesn't support multiple columns in IN/NOT IN
233
238
  def supports_multiple_column_in?
234
239
  false
@@ -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 insert_values_sql(sql)
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 (except for +:index+, use a
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
- @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
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 (except for +:index+, use a
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