sequel 5.45.0 → 5.77.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 +434 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- 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/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +41 -11
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +109 -19
@@ -19,6 +19,9 @@ module Sequel
|
|
19
19
|
module Postgres
|
20
20
|
Sequel::Database.set_shared_adapter_scheme(:postgres, self)
|
21
21
|
|
22
|
+
# Exception class ranged when literalizing integers outside the bigint/int8 range.
|
23
|
+
class IntegerOutsideBigintRange < InvalidValue; end
|
24
|
+
|
22
25
|
NAN = 0.0/0.0
|
23
26
|
PLUS_INFINITY = 1.0/0.0
|
24
27
|
MINUS_INFINITY = -1.0/0.0
|
@@ -83,11 +86,22 @@ module Sequel
|
|
83
86
|
def primary_key(table)
|
84
87
|
:id
|
85
88
|
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
# Handle NoMethodErrors when parsing schema due to output_identifier
|
93
|
+
# being called with nil when the Database fetch results are not set
|
94
|
+
# to what schema parsing expects.
|
95
|
+
def schema_parse_table(table, opts=OPTS)
|
96
|
+
super
|
97
|
+
rescue NoMethodError
|
98
|
+
[]
|
99
|
+
end
|
86
100
|
end
|
87
101
|
|
88
102
|
def self.mock_adapter_setup(db)
|
89
103
|
db.instance_exec do
|
90
|
-
@server_version =
|
104
|
+
@server_version = 150000
|
91
105
|
initialize_postgres_adapter
|
92
106
|
extend(MockAdapterDatabaseMethods)
|
93
107
|
end
|
@@ -230,7 +244,6 @@ module Sequel
|
|
230
244
|
module DatabaseMethods
|
231
245
|
include UnmodifiedIdentifiers::DatabaseMethods
|
232
246
|
|
233
|
-
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
234
247
|
FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
|
235
248
|
ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
|
236
249
|
ON_COMMIT.each_value(&:freeze)
|
@@ -254,7 +267,7 @@ module Sequel
|
|
254
267
|
WHERE cons.contype = 'p'
|
255
268
|
AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
|
256
269
|
end_sql
|
257
|
-
).strip.gsub(/\s+/, ' ').freeze
|
270
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
258
271
|
|
259
272
|
# SQL fragment for determining primary key column for the given table. Only
|
260
273
|
# returns the first primary key if the table has a composite primary key.
|
@@ -267,7 +280,7 @@ module Sequel
|
|
267
280
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
268
281
|
AND pg_index.indisprimary = 't'
|
269
282
|
end_sql
|
270
|
-
).strip.gsub(/\s+/, ' ').freeze
|
283
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
271
284
|
|
272
285
|
# SQL fragment for getting sequence associated with table's
|
273
286
|
# primary key, assuming it was a serial primary key column.
|
@@ -285,7 +298,7 @@ module Sequel
|
|
285
298
|
AND attr.attrelid = t.oid
|
286
299
|
AND cons.contype = 'p'
|
287
300
|
end_sql
|
288
|
-
).strip.gsub(/\s+/, ' ').freeze
|
301
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
289
302
|
|
290
303
|
# A hash of conversion procs, keyed by type integer (oid) and
|
291
304
|
# having callable values for the conversion proc for that type.
|
@@ -319,14 +332,8 @@ module Sequel
|
|
319
332
|
def check_constraints(table)
|
320
333
|
m = output_identifier_meth
|
321
334
|
|
322
|
-
rows = metadata_dataset.
|
323
|
-
from{pg_constraint.as(:co)}.
|
324
|
-
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
325
|
-
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
326
|
-
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
327
|
-
|
328
335
|
hash = {}
|
329
|
-
|
336
|
+
_check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
|
330
337
|
constraint = m.call(row[:constraint])
|
331
338
|
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
332
339
|
entry[:columns] << m.call(row[:column]) if row[:column]
|
@@ -365,9 +372,10 @@ module Sequel
|
|
365
372
|
|
366
373
|
table_oid = regclass_oid(table)
|
367
374
|
im = input_identifier_meth
|
368
|
-
unless column =
|
375
|
+
unless column = (opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
|
369
376
|
raise Error, "could not determine column to convert from serial to identity automatically"
|
370
377
|
end
|
378
|
+
column = im.call(column)
|
371
379
|
|
372
380
|
column_num = ds.from(:pg_attribute).
|
373
381
|
where(:attrelid=>table_oid, :attname=>column).
|
@@ -414,6 +422,7 @@ module Sequel
|
|
414
422
|
# 2 :: argument name
|
415
423
|
# 3 :: argument mode (e.g. in, out, inout)
|
416
424
|
# :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
|
425
|
+
# :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
|
417
426
|
# :cost :: The estimated cost of the function, used by the query planner.
|
418
427
|
# :language :: The language the function uses. SQL is the default.
|
419
428
|
# :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
|
@@ -479,6 +488,7 @@ module Sequel
|
|
479
488
|
# :each_row :: Calls the trigger for each row instead of for each statement.
|
480
489
|
# :events :: Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
|
481
490
|
# the trigger is called for insert, update, or delete.
|
491
|
+
# :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
|
482
492
|
# :when :: A filter to use for the trigger
|
483
493
|
def create_trigger(table, name, function, opts=OPTS)
|
484
494
|
self << create_trigger_sql(table, name, function, opts)
|
@@ -488,6 +498,25 @@ module Sequel
|
|
488
498
|
:postgres
|
489
499
|
end
|
490
500
|
|
501
|
+
# For constraints that are deferrable, defer constraints until
|
502
|
+
# transaction commit. Options:
|
503
|
+
#
|
504
|
+
# :constraints :: An identifier of the constraint, or an array of
|
505
|
+
# identifiers for constraints, to apply this
|
506
|
+
# change to specific constraints.
|
507
|
+
# :server :: The server/shard on which to run the query.
|
508
|
+
#
|
509
|
+
# Examples:
|
510
|
+
#
|
511
|
+
# DB.defer_constraints
|
512
|
+
# # SET CONSTRAINTS ALL DEFERRED
|
513
|
+
#
|
514
|
+
# DB.defer_constraints(constraints: [:c1, Sequel[:sc][:c2]])
|
515
|
+
# # SET CONSTRAINTS "c1", "sc"."s2" DEFERRED
|
516
|
+
def defer_constraints(opts=OPTS)
|
517
|
+
_set_constraints(' DEFERRED', opts)
|
518
|
+
end
|
519
|
+
|
491
520
|
# Use PostgreSQL's DO syntax to execute an anonymous code block. The code should
|
492
521
|
# be the literal code string to use in the underlying procedural language. Options:
|
493
522
|
#
|
@@ -549,63 +578,12 @@ module Sequel
|
|
549
578
|
def foreign_key_list(table, opts=OPTS)
|
550
579
|
m = output_identifier_meth
|
551
580
|
schema, _ = opts.fetch(:schema, schema_and_table(table))
|
552
|
-
oid = regclass_oid(table)
|
553
|
-
reverse = opts[:reverse]
|
554
|
-
|
555
|
-
if reverse
|
556
|
-
ctable = Sequel[:att2]
|
557
|
-
cclass = Sequel[:cl2]
|
558
|
-
rtable = Sequel[:att]
|
559
|
-
rclass = Sequel[:cl]
|
560
|
-
else
|
561
|
-
ctable = Sequel[:att]
|
562
|
-
cclass = Sequel[:cl]
|
563
|
-
rtable = Sequel[:att2]
|
564
|
-
rclass = Sequel[:cl2]
|
565
|
-
end
|
566
|
-
|
567
|
-
if server_version >= 90500
|
568
|
-
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
569
|
-
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
570
|
-
else
|
571
|
-
range = 0...32
|
572
|
-
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
573
|
-
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
574
|
-
end
|
575
|
-
|
576
|
-
ds = metadata_dataset.
|
577
|
-
from{pg_constraint.as(:co)}.
|
578
|
-
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
579
|
-
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
580
|
-
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
581
|
-
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
582
|
-
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
583
|
-
order{[co[:conname], cpos]}.
|
584
|
-
where{{
|
585
|
-
cl[:relkind]=>'r',
|
586
|
-
co[:contype]=>'f',
|
587
|
-
cl[:oid]=>oid,
|
588
|
-
cpos=>rpos
|
589
|
-
}}.
|
590
|
-
select{[
|
591
|
-
co[:conname].as(:name),
|
592
|
-
ctable[:attname].as(:column),
|
593
|
-
co[:confupdtype].as(:on_update),
|
594
|
-
co[:confdeltype].as(:on_delete),
|
595
|
-
cl2[:relname].as(:table),
|
596
|
-
rtable[:attname].as(:refcolumn),
|
597
|
-
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
598
|
-
nsp[:nspname].as(:schema)
|
599
|
-
]}
|
600
|
-
|
601
|
-
if reverse
|
602
|
-
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
603
|
-
end
|
604
581
|
|
605
582
|
h = {}
|
606
583
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
584
|
+
reverse = opts[:reverse]
|
607
585
|
|
608
|
-
|
586
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
609
587
|
if reverse
|
610
588
|
key = [row[:schema], row[:table], row[:name]]
|
611
589
|
else
|
@@ -640,45 +618,44 @@ module Sequel
|
|
640
618
|
def freeze
|
641
619
|
server_version
|
642
620
|
supports_prepared_transactions?
|
621
|
+
_schema_ds
|
622
|
+
_select_serial_sequence_ds
|
623
|
+
_select_custom_sequence_ds
|
624
|
+
_select_pk_ds
|
625
|
+
_indexes_ds
|
626
|
+
_check_constraints_ds
|
627
|
+
_foreign_key_list_ds
|
628
|
+
_reverse_foreign_key_list_ds
|
643
629
|
@conversion_procs.freeze
|
644
630
|
super
|
645
631
|
end
|
646
632
|
|
633
|
+
# Immediately apply deferrable constraints.
|
634
|
+
#
|
635
|
+
# :constraints :: An identifier of the constraint, or an array of
|
636
|
+
# identifiers for constraints, to apply this
|
637
|
+
# change to specific constraints.
|
638
|
+
# :server :: The server/shard on which to run the query.
|
639
|
+
#
|
640
|
+
# Examples:
|
641
|
+
#
|
642
|
+
# DB.immediate_constraints
|
643
|
+
# # SET CONSTRAINTS ALL IMMEDIATE
|
644
|
+
#
|
645
|
+
# DB.immediate_constraints(constraints: [:c1, Sequel[:sc][:c2]])
|
646
|
+
# # SET CONSTRAINTS "c1", "sc"."s2" IMMEDIATE
|
647
|
+
def immediate_constraints(opts=OPTS)
|
648
|
+
_set_constraints(' IMMEDIATE', opts)
|
649
|
+
end
|
650
|
+
|
647
651
|
# Use the pg_* system tables to determine indexes on a table
|
648
652
|
def indexes(table, opts=OPTS)
|
649
653
|
m = output_identifier_meth
|
650
|
-
|
651
|
-
|
652
|
-
if server_version >= 90500
|
653
|
-
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
654
|
-
else
|
655
|
-
range = 0...32
|
656
|
-
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
657
|
-
end
|
658
|
-
|
659
|
-
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
660
|
-
|
661
|
-
ds = metadata_dataset.
|
662
|
-
from{pg_class.as(:tab)}.
|
663
|
-
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
664
|
-
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
665
|
-
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
666
|
-
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
667
|
-
where{{
|
668
|
-
indc[:relkind]=>'i',
|
669
|
-
ind[:indisprimary]=>false,
|
670
|
-
:indexprs=>nil,
|
671
|
-
:indisvalid=>true,
|
672
|
-
tab[:oid]=>oid}}.
|
673
|
-
order(*order).
|
674
|
-
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
675
|
-
|
676
|
-
ds = ds.where(:indpred=>nil) unless opts[:include_partial]
|
677
|
-
ds = ds.where(:indisready=>true) if server_version >= 80300
|
678
|
-
ds = ds.where(:indislive=>true) if server_version >= 90300
|
654
|
+
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
655
|
+
cond[:indpred] = nil unless opts[:include_partial]
|
679
656
|
|
680
657
|
indexes = {}
|
681
|
-
|
658
|
+
_indexes_ds.where_each(cond) do |r|
|
682
659
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
683
660
|
i[:columns] << m.call(r[:column])
|
684
661
|
end
|
@@ -711,8 +688,7 @@ module Sequel
|
|
711
688
|
def primary_key(table, opts=OPTS)
|
712
689
|
quoted_table = quote_schema_table(table)
|
713
690
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
714
|
-
|
715
|
-
value = fetch(sql).single_value
|
691
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
716
692
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
717
693
|
end
|
718
694
|
|
@@ -720,24 +696,21 @@ module Sequel
|
|
720
696
|
def primary_key_sequence(table, opts=OPTS)
|
721
697
|
quoted_table = quote_schema_table(table)
|
722
698
|
Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
|
723
|
-
|
724
|
-
if pks =
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
|
729
|
-
if pks = fetch(sql).single_record
|
730
|
-
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
731
|
-
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
732
|
-
end
|
699
|
+
cond = {Sequel[:t][:oid] => regclass_oid(table, opts)}
|
700
|
+
value = if pks = _select_serial_sequence_ds.first(cond)
|
701
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
|
702
|
+
elsif pks = _select_custom_sequence_ds.first(cond)
|
703
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
733
704
|
end
|
705
|
+
|
706
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
734
707
|
end
|
735
708
|
|
736
709
|
# Refresh the materialized view with the given name.
|
737
710
|
#
|
738
711
|
# DB.refresh_view(:items_view)
|
739
712
|
# # REFRESH MATERIALIZED VIEW items_view
|
740
|
-
# DB.refresh_view(:items_view, :
|
713
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
741
714
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
742
715
|
def refresh_view(name, opts=OPTS)
|
743
716
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
@@ -756,10 +729,12 @@ module Sequel
|
|
756
729
|
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
757
730
|
increment_by = :seqincrement
|
758
731
|
min_value = :seqmin
|
732
|
+
# :nocov:
|
759
733
|
else
|
760
734
|
seq_ds = metadata_dataset.from(LiteralString.new(seq))
|
761
735
|
increment_by = :increment_by
|
762
736
|
min_value = :min_value
|
737
|
+
# :nocov:
|
763
738
|
end
|
764
739
|
|
765
740
|
get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
|
@@ -772,7 +747,9 @@ module Sequel
|
|
772
747
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
773
748
|
# managing incrementing primary keys.
|
774
749
|
def serial_primary_key_options
|
750
|
+
# :nocov:
|
775
751
|
auto_increment_key = server_version >= 100002 ? :identity : :serial
|
752
|
+
# :nocov:
|
776
753
|
{:primary_key => true, auto_increment_key => true, :type=>Integer}
|
777
754
|
end
|
778
755
|
|
@@ -865,6 +842,7 @@ module Sequel
|
|
865
842
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
866
843
|
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
867
844
|
def values(v)
|
845
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
868
846
|
@default_dataset.clone(:values=>v)
|
869
847
|
end
|
870
848
|
|
@@ -883,6 +861,245 @@ module Sequel
|
|
883
861
|
|
884
862
|
private
|
885
863
|
|
864
|
+
# Dataset used to retrieve CHECK constraint information
|
865
|
+
def _check_constraints_ds
|
866
|
+
@_check_constraints_ds ||= metadata_dataset.
|
867
|
+
from{pg_constraint.as(:co)}.
|
868
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
869
|
+
where(:contype=>'c').
|
870
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
871
|
+
end
|
872
|
+
|
873
|
+
# Dataset used to retrieve foreign keys referenced by a table
|
874
|
+
def _foreign_key_list_ds
|
875
|
+
@_foreign_key_list_ds ||= __foreign_key_list_ds(false)
|
876
|
+
end
|
877
|
+
|
878
|
+
# Dataset used to retrieve foreign keys referencing a table
|
879
|
+
def _reverse_foreign_key_list_ds
|
880
|
+
@_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
|
881
|
+
end
|
882
|
+
|
883
|
+
# Build dataset used for foreign key list methods.
|
884
|
+
def __foreign_key_list_ds(reverse)
|
885
|
+
if reverse
|
886
|
+
ctable = Sequel[:att2]
|
887
|
+
cclass = Sequel[:cl2]
|
888
|
+
rtable = Sequel[:att]
|
889
|
+
rclass = Sequel[:cl]
|
890
|
+
else
|
891
|
+
ctable = Sequel[:att]
|
892
|
+
cclass = Sequel[:cl]
|
893
|
+
rtable = Sequel[:att2]
|
894
|
+
rclass = Sequel[:cl2]
|
895
|
+
end
|
896
|
+
|
897
|
+
if server_version >= 90500
|
898
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
899
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
900
|
+
# :nocov:
|
901
|
+
else
|
902
|
+
range = 0...32
|
903
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
904
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
905
|
+
# :nocov:
|
906
|
+
end
|
907
|
+
|
908
|
+
ds = metadata_dataset.
|
909
|
+
from{pg_constraint.as(:co)}.
|
910
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
911
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
912
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
913
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
914
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
915
|
+
order{[co[:conname], cpos]}.
|
916
|
+
where{{
|
917
|
+
cl[:relkind]=>%w'r p',
|
918
|
+
co[:contype]=>'f',
|
919
|
+
cpos=>rpos
|
920
|
+
}}.
|
921
|
+
select{[
|
922
|
+
co[:conname].as(:name),
|
923
|
+
ctable[:attname].as(:column),
|
924
|
+
co[:confupdtype].as(:on_update),
|
925
|
+
co[:confdeltype].as(:on_delete),
|
926
|
+
cl2[:relname].as(:table),
|
927
|
+
rtable[:attname].as(:refcolumn),
|
928
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
929
|
+
nsp[:nspname].as(:schema)
|
930
|
+
]}
|
931
|
+
|
932
|
+
if reverse
|
933
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
934
|
+
end
|
935
|
+
|
936
|
+
ds
|
937
|
+
end
|
938
|
+
|
939
|
+
# Dataset used to retrieve index information
|
940
|
+
def _indexes_ds
|
941
|
+
@_indexes_ds ||= begin
|
942
|
+
if server_version >= 90500
|
943
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
944
|
+
# :nocov:
|
945
|
+
else
|
946
|
+
range = 0...32
|
947
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
948
|
+
# :nocov:
|
949
|
+
end
|
950
|
+
|
951
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
952
|
+
|
953
|
+
ds = metadata_dataset.
|
954
|
+
from{pg_class.as(:tab)}.
|
955
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
956
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
957
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
958
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
959
|
+
where{{
|
960
|
+
indc[:relkind]=>%w'i I',
|
961
|
+
ind[:indisprimary]=>false,
|
962
|
+
:indexprs=>nil,
|
963
|
+
:indisvalid=>true}}.
|
964
|
+
order(*order).
|
965
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
966
|
+
|
967
|
+
# :nocov:
|
968
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
969
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
970
|
+
# :nocov:
|
971
|
+
|
972
|
+
ds
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
# Dataset used to determine custom serial sequences for tables
|
977
|
+
def _select_custom_sequence_ds
|
978
|
+
@_select_custom_sequence_ds ||= metadata_dataset.
|
979
|
+
from{pg_class.as(:t)}.
|
980
|
+
join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
|
981
|
+
join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
|
982
|
+
join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
|
983
|
+
join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
|
984
|
+
where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
|
985
|
+
select{
|
986
|
+
expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
|
987
|
+
[
|
988
|
+
name[:nspname].as(:schema),
|
989
|
+
Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
|
990
|
+
]
|
991
|
+
}
|
992
|
+
end
|
993
|
+
|
994
|
+
# Dataset used to determine normal serial sequences for tables
|
995
|
+
def _select_serial_sequence_ds
|
996
|
+
@_serial_sequence_ds ||= metadata_dataset.
|
997
|
+
from{[
|
998
|
+
pg_class.as(:seq),
|
999
|
+
pg_attribute.as(:attr),
|
1000
|
+
pg_depend.as(:dep),
|
1001
|
+
pg_namespace.as(:name),
|
1002
|
+
pg_constraint.as(:cons),
|
1003
|
+
pg_class.as(:t)
|
1004
|
+
]}.
|
1005
|
+
where{[
|
1006
|
+
[seq[:oid], dep[:objid]],
|
1007
|
+
[seq[:relnamespace], name[:oid]],
|
1008
|
+
[seq[:relkind], 'S'],
|
1009
|
+
[attr[:attrelid], dep[:refobjid]],
|
1010
|
+
[attr[:attnum], dep[:refobjsubid]],
|
1011
|
+
[attr[:attrelid], cons[:conrelid]],
|
1012
|
+
[attr[:attnum], cons[:conkey].sql_subscript(1)],
|
1013
|
+
[attr[:attrelid], t[:oid]],
|
1014
|
+
[cons[:contype], 'p']
|
1015
|
+
]}.
|
1016
|
+
select{[
|
1017
|
+
name[:nspname].as(:schema),
|
1018
|
+
seq[:relname].as(:sequence)
|
1019
|
+
]}
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Dataset used to determine primary keys for tables
|
1023
|
+
def _select_pk_ds
|
1024
|
+
@_select_pk_ds ||= metadata_dataset.
|
1025
|
+
from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
|
1026
|
+
where{[
|
1027
|
+
[pg_class[:oid], pg_attribute[:attrelid]],
|
1028
|
+
[pg_class[:relnamespace], pg_namespace[:oid]],
|
1029
|
+
[pg_class[:oid], pg_index[:indrelid]],
|
1030
|
+
[pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
|
1031
|
+
[pg_index[:indisprimary], 't']
|
1032
|
+
]}.
|
1033
|
+
select{pg_attribute[:attname].as(:pk)}
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
# Dataset used to get schema for tables
|
1037
|
+
def _schema_ds
|
1038
|
+
@_schema_ds ||= begin
|
1039
|
+
ds = metadata_dataset.select{[
|
1040
|
+
pg_attribute[:attname].as(:name),
|
1041
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1042
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1043
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1044
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1045
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1046
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1047
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key),
|
1048
|
+
Sequel[:pg_type][:typtype],
|
1049
|
+
(~Sequel[Sequel[:elementtype][:oid]=>nil]).as(:is_array),
|
1050
|
+
]}.
|
1051
|
+
from(:pg_class).
|
1052
|
+
join(:pg_attribute, :attrelid=>:oid).
|
1053
|
+
join(:pg_type, :oid=>:atttypid).
|
1054
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1055
|
+
left_outer_join(Sequel[:pg_type].as(:elementtype), :typarray=>Sequel[:pg_type][:oid]).
|
1056
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1057
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1058
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
1059
|
+
where{pg_attribute[:attnum] > 0}.
|
1060
|
+
order{pg_attribute[:attnum]}
|
1061
|
+
|
1062
|
+
# :nocov:
|
1063
|
+
if server_version > 100000
|
1064
|
+
# :nocov:
|
1065
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
1066
|
+
|
1067
|
+
# :nocov:
|
1068
|
+
if server_version > 120000
|
1069
|
+
# :nocov:
|
1070
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
|
1074
|
+
ds
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
# Internals of defer_constraints/immediate_constraints
|
1079
|
+
def _set_constraints(type, opts)
|
1080
|
+
execute_ddl(_set_constraints_sql(type, opts), opts)
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# SQL to use for SET CONSTRAINTS
|
1084
|
+
def _set_constraints_sql(type, opts)
|
1085
|
+
sql = String.new
|
1086
|
+
sql << "SET CONSTRAINTS "
|
1087
|
+
if constraints = opts[:constraints]
|
1088
|
+
dataset.send(:source_list_append, sql, Array(constraints))
|
1089
|
+
else
|
1090
|
+
sql << "ALL"
|
1091
|
+
end
|
1092
|
+
sql << type
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
# Consider lock or statement timeout errors as evidence that the table exists
|
1096
|
+
# but is locked.
|
1097
|
+
def _table_exists?(ds)
|
1098
|
+
super
|
1099
|
+
rescue DatabaseError => e
|
1100
|
+
raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
|
1101
|
+
end
|
1102
|
+
|
886
1103
|
def alter_table_add_column_sql(table, op)
|
887
1104
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
888
1105
|
end
|
@@ -1116,6 +1333,7 @@ module Sequel
|
|
1116
1333
|
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
1117
1334
|
#{'STRICT' if opts[:strict]}
|
1118
1335
|
#{'SECURITY DEFINER' if opts[:security_definer]}
|
1336
|
+
#{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
|
1119
1337
|
#{"COST #{opts[:cost]}" if opts[:cost]}
|
1120
1338
|
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
1121
1339
|
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
@@ -1149,7 +1367,7 @@ module Sequel
|
|
1149
1367
|
when :hash
|
1150
1368
|
mod, remainder = generator.hash_values
|
1151
1369
|
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
1152
|
-
when :default
|
1370
|
+
else # when :default
|
1153
1371
|
sql << " DEFAULT"
|
1154
1372
|
end
|
1155
1373
|
|
@@ -1237,13 +1455,17 @@ module Sequel
|
|
1237
1455
|
raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
|
1238
1456
|
filter = " WHEN #{filter_expr(filter)}"
|
1239
1457
|
end
|
1240
|
-
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
1458
|
+
"CREATE #{'OR REPLACE ' if opts[:replace]}TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
1241
1459
|
end
|
1242
1460
|
|
1243
1461
|
# DDL fragment for initial part of CREATE VIEW statement
|
1244
1462
|
def create_view_prefix_sql(name, options)
|
1245
1463
|
sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
|
1246
1464
|
|
1465
|
+
if options[:security_invoker]
|
1466
|
+
sql += " WITH (security_invoker)"
|
1467
|
+
end
|
1468
|
+
|
1247
1469
|
if tablespace = options[:tablespace]
|
1248
1470
|
sql += " TABLESPACE #{quote_identifier(tablespace)}"
|
1249
1471
|
end
|
@@ -1291,7 +1513,11 @@ module Sequel
|
|
1291
1513
|
# currently visible schemas.
|
1292
1514
|
def filter_schema(ds, opts)
|
1293
1515
|
expr = if schema = opts[:schema]
|
1294
|
-
schema.
|
1516
|
+
if schema.is_a?(SQL::Identifier)
|
1517
|
+
schema.value.to_s
|
1518
|
+
else
|
1519
|
+
schema.to_s
|
1520
|
+
end
|
1295
1521
|
else
|
1296
1522
|
Sequel.function(:any, Sequel.function(:current_schemas, false))
|
1297
1523
|
end
|
@@ -1301,16 +1527,20 @@ module Sequel
|
|
1301
1527
|
def index_definition_sql(table_name, index)
|
1302
1528
|
cols = index[:columns]
|
1303
1529
|
index_name = index[:name] || default_index_name(table_name, cols)
|
1530
|
+
|
1304
1531
|
expr = if o = index[:opclass]
|
1305
1532
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
1306
1533
|
else
|
1307
1534
|
literal(Array(cols))
|
1308
1535
|
end
|
1536
|
+
|
1309
1537
|
if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
|
1310
1538
|
unique = "UNIQUE " if index[:unique]
|
1311
1539
|
index_type = index[:type]
|
1312
1540
|
filter = index[:where] || index[:filter]
|
1313
1541
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
1542
|
+
nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
|
1543
|
+
|
1314
1544
|
case index_type
|
1315
1545
|
when :full_text
|
1316
1546
|
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
@@ -1318,7 +1548,8 @@ module Sequel
|
|
1318
1548
|
when :spatial
|
1319
1549
|
index_type = :gist
|
1320
1550
|
end
|
1321
|
-
|
1551
|
+
|
1552
|
+
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
|
1322
1553
|
end
|
1323
1554
|
|
1324
1555
|
# Setup datastructures shared by all postgres adapters.
|
@@ -1335,7 +1566,7 @@ module Sequel
|
|
1335
1566
|
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
1336
1567
|
ds = filter_schema(ds, opts)
|
1337
1568
|
m = output_identifier_meth
|
1338
|
-
if
|
1569
|
+
if defined?(yield)
|
1339
1570
|
yield(ds)
|
1340
1571
|
elsif opts[:qualify]
|
1341
1572
|
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
@@ -1344,11 +1575,6 @@ module Sequel
|
|
1344
1575
|
end
|
1345
1576
|
end
|
1346
1577
|
|
1347
|
-
# Use a dollar sign instead of question mark for the argument placeholder.
|
1348
|
-
def prepared_arg_placeholder
|
1349
|
-
PREPARED_ARG_PLACEHOLDER
|
1350
|
-
end
|
1351
|
-
|
1352
1578
|
# Return an expression the oid for the table expr. Used by the metadata parsing
|
1353
1579
|
# code to disambiguate unqualified tables.
|
1354
1580
|
def regclass_oid(expr, opts=OPTS)
|
@@ -1382,11 +1608,12 @@ module Sequel
|
|
1382
1608
|
end
|
1383
1609
|
|
1384
1610
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
1385
|
-
# a rename table operation, so
|
1611
|
+
# a rename table operation, so specifying a new schema in new_name will not have an effect.
|
1386
1612
|
def rename_table_sql(name, new_name)
|
1387
1613
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
1388
1614
|
end
|
1389
1615
|
|
1616
|
+
# Handle interval and citext types.
|
1390
1617
|
def schema_column_type(db_type)
|
1391
1618
|
case db_type
|
1392
1619
|
when /\Ainterval\z/io
|
@@ -1398,39 +1625,48 @@ module Sequel
|
|
1398
1625
|
end
|
1399
1626
|
end
|
1400
1627
|
|
1628
|
+
# The schema :type entry to use for array types.
|
1629
|
+
def schema_array_type(db_type)
|
1630
|
+
:array
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
# The schema :type entry to use for row/composite types.
|
1634
|
+
def schema_composite_type(db_type)
|
1635
|
+
:composite
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
# The schema :type entry to use for enum types.
|
1639
|
+
def schema_enum_type(db_type)
|
1640
|
+
:enum
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
# The schema :type entry to use for range types.
|
1644
|
+
def schema_range_type(db_type)
|
1645
|
+
:range
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
# The schema :type entry to use for multirange types.
|
1649
|
+
def schema_multirange_type(db_type)
|
1650
|
+
:multirange
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
MIN_DATE = Date.new(-4713, 11, 24)
|
1654
|
+
MAX_DATE = Date.new(5874897, 12, 31)
|
1655
|
+
MIN_TIMESTAMP = Time.utc(-4713, 11, 24).freeze
|
1656
|
+
MAX_TIMESTAMP = (Time.utc(294277) - Rational(1, 1000000)).freeze
|
1657
|
+
TYPTYPE_METHOD_MAP = {
|
1658
|
+
'c' => :schema_composite_type,
|
1659
|
+
'e' => :schema_enum_type,
|
1660
|
+
'r' => :schema_range_type,
|
1661
|
+
'm' => :schema_multirange_type,
|
1662
|
+
}
|
1663
|
+
TYPTYPE_METHOD_MAP.default = :schema_column_type
|
1664
|
+
TYPTYPE_METHOD_MAP.freeze
|
1401
1665
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
1402
1666
|
def schema_parse_table(table_name, opts)
|
1403
1667
|
m = output_identifier_meth(opts[:dataset])
|
1404
|
-
oid = regclass_oid(table_name, opts)
|
1405
|
-
ds = metadata_dataset.select{[
|
1406
|
-
pg_attribute[:attname].as(:name),
|
1407
|
-
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1408
|
-
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1409
|
-
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1410
|
-
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1411
|
-
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1412
|
-
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1413
|
-
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
1414
|
-
from(:pg_class).
|
1415
|
-
join(:pg_attribute, :attrelid=>:oid).
|
1416
|
-
join(:pg_type, :oid=>:atttypid).
|
1417
|
-
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1418
|
-
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1419
|
-
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1420
|
-
where{{pg_attribute[:attisdropped]=>false}}.
|
1421
|
-
where{pg_attribute[:attnum] > 0}.
|
1422
|
-
where{{pg_class[:oid]=>oid}}.
|
1423
|
-
order{pg_attribute[:attnum]}
|
1424
|
-
|
1425
|
-
if server_version > 100000
|
1426
|
-
ds = ds.select_append{pg_attribute[:attidentity]}
|
1427
|
-
|
1428
|
-
if server_version > 120000
|
1429
|
-
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1430
|
-
end
|
1431
|
-
end
|
1432
1668
|
|
1433
|
-
|
1669
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
1434
1670
|
row[:default] = nil if blank_object?(row[:default])
|
1435
1671
|
if row[:base_oid]
|
1436
1672
|
row[:domain_oid] = row[:oid]
|
@@ -1441,11 +1677,33 @@ module Sequel
|
|
1441
1677
|
row.delete(:base_oid)
|
1442
1678
|
row.delete(:db_base_type)
|
1443
1679
|
end
|
1444
|
-
|
1680
|
+
|
1681
|
+
db_type = row[:db_type]
|
1682
|
+
row[:type] = if row.delete(:is_array)
|
1683
|
+
schema_array_type(db_type)
|
1684
|
+
else
|
1685
|
+
send(TYPTYPE_METHOD_MAP[row.delete(:typtype)], db_type)
|
1686
|
+
end
|
1445
1687
|
identity = row.delete(:attidentity)
|
1446
1688
|
if row[:primary_key]
|
1447
1689
|
row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
|
1448
1690
|
end
|
1691
|
+
|
1692
|
+
# :nocov:
|
1693
|
+
if server_version >= 90600
|
1694
|
+
# :nocov:
|
1695
|
+
case row[:oid]
|
1696
|
+
when 1082
|
1697
|
+
row[:min_value] = MIN_DATE
|
1698
|
+
row[:max_value] = MAX_DATE
|
1699
|
+
when 1184, 1114
|
1700
|
+
if Sequel.datetime_class == Time
|
1701
|
+
row[:min_value] = MIN_TIMESTAMP
|
1702
|
+
row[:max_value] = MAX_TIMESTAMP
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
end
|
1706
|
+
|
1449
1707
|
[m.call(row.delete(:name)), row]
|
1450
1708
|
end
|
1451
1709
|
end
|
@@ -1503,9 +1761,9 @@ module Sequel
|
|
1503
1761
|
if column[:text]
|
1504
1762
|
:text
|
1505
1763
|
elsif column[:fixed]
|
1506
|
-
"char(#{column[:size]||
|
1764
|
+
"char(#{column[:size]||default_string_column_size})"
|
1507
1765
|
elsif column[:text] == false || column[:size]
|
1508
|
-
"varchar(#{column[:size]||
|
1766
|
+
"varchar(#{column[:size]||default_string_column_size})"
|
1509
1767
|
else
|
1510
1768
|
:text
|
1511
1769
|
end
|
@@ -1513,7 +1771,9 @@ module Sequel
|
|
1513
1771
|
|
1514
1772
|
# PostgreSQL 9.4+ supports views with check option.
|
1515
1773
|
def view_with_check_option_support
|
1774
|
+
# :nocov:
|
1516
1775
|
:local if server_version >= 90400
|
1776
|
+
# :nocov:
|
1517
1777
|
end
|
1518
1778
|
end
|
1519
1779
|
|
@@ -1524,7 +1784,7 @@ module Sequel
|
|
1524
1784
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
1525
1785
|
|
1526
1786
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1527
|
-
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']])
|
1787
|
+
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']])
|
1528
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']])
|
1529
1789
|
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']])
|
1530
1790
|
|
@@ -1551,8 +1811,6 @@ module Sequel
|
|
1551
1811
|
literal_append(sql, args[0])
|
1552
1812
|
sql << ' ' << op.to_s << ' '
|
1553
1813
|
literal_append(sql, args[1])
|
1554
|
-
sql << " ESCAPE "
|
1555
|
-
literal_append(sql, "\\")
|
1556
1814
|
sql << ')'
|
1557
1815
|
else
|
1558
1816
|
super
|
@@ -1577,6 +1835,12 @@ module Sequel
|
|
1577
1835
|
clone(:disable_insert_returning=>true)
|
1578
1836
|
end
|
1579
1837
|
|
1838
|
+
# Always return false when using VALUES
|
1839
|
+
def empty?
|
1840
|
+
return false if @opts[:values]
|
1841
|
+
super
|
1842
|
+
end
|
1843
|
+
|
1580
1844
|
# Return the results of an EXPLAIN query as a string
|
1581
1845
|
def explain(opts=OPTS)
|
1582
1846
|
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
@@ -1600,7 +1864,7 @@ module Sequel
|
|
1600
1864
|
# :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
|
1601
1865
|
# returned rows also include the exact phrase used.
|
1602
1866
|
# :rank :: Set to true to order by the rank, so that closer matches are returned first.
|
1603
|
-
# :to_tsquery :: Can be set to :plain or :
|
1867
|
+
# :to_tsquery :: Can be set to :plain, :phrase, or :websearch to specify the function to use to
|
1604
1868
|
# convert the terms to a ts_query.
|
1605
1869
|
# :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
|
1606
1870
|
# tsquery, and can be used directly in the query.
|
@@ -1620,6 +1884,8 @@ module Sequel
|
|
1620
1884
|
query_func = case to_tsquery = opts[:to_tsquery]
|
1621
1885
|
when :phrase, :plain
|
1622
1886
|
:"#{to_tsquery}to_tsquery"
|
1887
|
+
when :websearch
|
1888
|
+
:"websearch_to_tsquery"
|
1623
1889
|
else
|
1624
1890
|
(opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
1625
1891
|
end
|
@@ -1727,13 +1993,22 @@ module Sequel
|
|
1727
1993
|
ds.insert_sql(*values)
|
1728
1994
|
end
|
1729
1995
|
|
1996
|
+
# Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
|
1997
|
+
# USING columns.
|
1998
|
+
def join_table(type, table, expr=nil, options=OPTS, &block)
|
1999
|
+
if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
|
2000
|
+
options = options.merge(:join_using=>true)
|
2001
|
+
end
|
2002
|
+
super
|
2003
|
+
end
|
2004
|
+
|
1730
2005
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
1731
2006
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
1732
2007
|
# a new transaction, locks the table, and yields. If a block is not given,
|
1733
2008
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
1734
2009
|
# if you lock the table outside of an existing transaction. Returns nil.
|
1735
2010
|
def lock(mode, opts=OPTS)
|
1736
|
-
if
|
2011
|
+
if defined?(yield) # perform locking inside a transaction and yield to block
|
1737
2012
|
@db.transaction(opts){lock(mode, opts); yield}
|
1738
2013
|
else
|
1739
2014
|
sql = 'LOCK TABLE '.dup
|
@@ -1748,6 +2023,41 @@ module Sequel
|
|
1748
2023
|
nil
|
1749
2024
|
end
|
1750
2025
|
|
2026
|
+
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
2027
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2028
|
+
# use it as additional conditions for the match.
|
2029
|
+
#
|
2030
|
+
# merge_do_nothing_when_matched
|
2031
|
+
# # WHEN MATCHED THEN DO NOTHING
|
2032
|
+
#
|
2033
|
+
# merge_do_nothing_when_matched{a > 30}
|
2034
|
+
# # WHEN MATCHED AND (a > 30) THEN DO NOTHING
|
2035
|
+
def merge_do_nothing_when_matched(&block)
|
2036
|
+
_merge_when(:type=>:matched, &block)
|
2037
|
+
end
|
2038
|
+
|
2039
|
+
# Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
|
2040
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2041
|
+
# use it as additional conditions for the match.
|
2042
|
+
#
|
2043
|
+
# merge_do_nothing_when_not_matched
|
2044
|
+
# # WHEN NOT MATCHED THEN DO NOTHING
|
2045
|
+
#
|
2046
|
+
# merge_do_nothing_when_not_matched{a > 30}
|
2047
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
|
2048
|
+
def merge_do_nothing_when_not_matched(&block)
|
2049
|
+
_merge_when(:type=>:not_matched, &block)
|
2050
|
+
end
|
2051
|
+
|
2052
|
+
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
2053
|
+
def merge_insert(*values, &block)
|
2054
|
+
h = {:type=>:insert, :values=>values}
|
2055
|
+
if override = @opts[:override]
|
2056
|
+
h[:override] = insert_override_sql(String.new)
|
2057
|
+
end
|
2058
|
+
_merge_when(h, &block)
|
2059
|
+
end
|
2060
|
+
|
1751
2061
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
1752
2062
|
# always use the user supplied value, and an error is not raised for identity
|
1753
2063
|
# columns that are GENERATED ALWAYS.
|
@@ -1815,6 +2125,11 @@ module Sequel
|
|
1815
2125
|
true
|
1816
2126
|
end
|
1817
2127
|
|
2128
|
+
# PostgreSQL 15+ supports MERGE.
|
2129
|
+
def supports_merge?
|
2130
|
+
server_version >= 150000
|
2131
|
+
end
|
2132
|
+
|
1818
2133
|
# PostgreSQL supports NOWAIT.
|
1819
2134
|
def supports_nowait?
|
1820
2135
|
true
|
@@ -1835,10 +2150,14 @@ module Sequel
|
|
1835
2150
|
server_version >= 90500
|
1836
2151
|
end
|
1837
2152
|
|
2153
|
+
# :nocov:
|
2154
|
+
|
1838
2155
|
# PostgreSQL supports timezones in literal timestamps
|
1839
2156
|
def supports_timestamp_timezones?
|
2157
|
+
# SEQUEL6: Remove
|
1840
2158
|
true
|
1841
2159
|
end
|
2160
|
+
# :nocov:
|
1842
2161
|
|
1843
2162
|
# PostgreSQL 8.4+ supports WINDOW clause.
|
1844
2163
|
def supports_window_clause?
|
@@ -1860,6 +2179,8 @@ module Sequel
|
|
1860
2179
|
server_version >= 90000
|
1861
2180
|
when :groups, :exclude
|
1862
2181
|
server_version >= 110000
|
2182
|
+
else
|
2183
|
+
false
|
1863
2184
|
end
|
1864
2185
|
end
|
1865
2186
|
|
@@ -1902,12 +2223,10 @@ module Sequel
|
|
1902
2223
|
# Otherwise, return an array of hashes.
|
1903
2224
|
def _import(columns, values, opts=OPTS)
|
1904
2225
|
if @opts[:returning]
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1909
|
-
statements.map{|st| returning_fetch_rows(st)}
|
1910
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
2226
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
2227
|
+
# that there's only ever a single statement.
|
2228
|
+
sql = multi_insert_sql(columns, values)[0]
|
2229
|
+
returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
|
1911
2230
|
elsif opts[:return] == :primary_key
|
1912
2231
|
returning(insert_pk)._import(columns, values, opts)
|
1913
2232
|
else
|
@@ -1925,18 +2244,44 @@ module Sequel
|
|
1925
2244
|
|
1926
2245
|
private
|
1927
2246
|
|
2247
|
+
# Append the INSERT sql used in a MERGE
|
2248
|
+
def _merge_insert_sql(sql, data)
|
2249
|
+
sql << " THEN INSERT "
|
2250
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
2251
|
+
_insert_columns_sql(sql, columns)
|
2252
|
+
if override = data[:override]
|
2253
|
+
sql << override
|
2254
|
+
end
|
2255
|
+
_insert_values_sql(sql, values)
|
2256
|
+
end
|
2257
|
+
|
2258
|
+
def _merge_matched_sql(sql, data)
|
2259
|
+
sql << " THEN DO NOTHING"
|
2260
|
+
end
|
2261
|
+
alias _merge_not_matched_sql _merge_matched_sql
|
2262
|
+
|
1928
2263
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
1929
2264
|
def _truncate_sql(table)
|
1930
2265
|
to = @opts[:truncate_opts] || OPTS
|
1931
2266
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
1932
2267
|
end
|
1933
2268
|
|
2269
|
+
# Use from_self for aggregate dataset using VALUES.
|
2270
|
+
def aggreate_dataset_use_from_self?
|
2271
|
+
super || @opts[:values]
|
2272
|
+
end
|
2273
|
+
|
1934
2274
|
# Allow truncation of multiple source tables.
|
1935
2275
|
def check_truncation_allowed!
|
1936
2276
|
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
1937
2277
|
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
1938
2278
|
end
|
1939
2279
|
|
2280
|
+
# The strftime format to use when literalizing the time.
|
2281
|
+
def default_timestamp_format
|
2282
|
+
"'%Y-%m-%d %H:%M:%S.%6N%z'"
|
2283
|
+
end
|
2284
|
+
|
1940
2285
|
# Only include the primary table in the main delete clause
|
1941
2286
|
def delete_from_sql(sql)
|
1942
2287
|
sql << ' FROM '
|
@@ -1990,25 +2335,23 @@ module Sequel
|
|
1990
2335
|
|
1991
2336
|
# Return the primary key to use for RETURNING in an INSERT statement
|
1992
2337
|
def insert_pk
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
|
1997
|
-
|
1998
|
-
end
|
2338
|
+
(f = opts[:from]) && !f.empty? && (t = f.first)
|
2339
|
+
case t
|
2340
|
+
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
2341
|
+
if pk = db.primary_key(t)
|
2342
|
+
Sequel::SQL::Identifier.new(pk)
|
1999
2343
|
end
|
2000
2344
|
end
|
2001
2345
|
end
|
2002
2346
|
|
2003
2347
|
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
2004
|
-
def
|
2348
|
+
def insert_override_sql(sql)
|
2005
2349
|
case opts[:override]
|
2006
2350
|
when :system
|
2007
2351
|
sql << " OVERRIDING SYSTEM VALUE"
|
2008
2352
|
when :user
|
2009
2353
|
sql << " OVERRIDING USER VALUE"
|
2010
2354
|
end
|
2011
|
-
super
|
2012
2355
|
end
|
2013
2356
|
|
2014
2357
|
# For multiple table support, PostgreSQL requires at least
|
@@ -2023,6 +2366,17 @@ module Sequel
|
|
2023
2366
|
end
|
2024
2367
|
end
|
2025
2368
|
|
2369
|
+
# Support table aliases for USING columns
|
2370
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
2371
|
+
if using_columns.is_a?(SQL::AliasedExpression)
|
2372
|
+
super(sql, using_columns.expression)
|
2373
|
+
sql << ' AS '
|
2374
|
+
identifier_append(sql, using_columns.alias)
|
2375
|
+
else
|
2376
|
+
super
|
2377
|
+
end
|
2378
|
+
end
|
2379
|
+
|
2026
2380
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
2027
2381
|
def literal_blob_append(sql, v)
|
2028
2382
|
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
@@ -2046,6 +2400,22 @@ module Sequel
|
|
2046
2400
|
end
|
2047
2401
|
end
|
2048
2402
|
|
2403
|
+
# Handle Ruby integers outside PostgreSQL bigint range specially.
|
2404
|
+
def literal_integer(v)
|
2405
|
+
if v > 9223372036854775807 || v < -9223372036854775808
|
2406
|
+
literal_integer_outside_bigint_range(v)
|
2407
|
+
else
|
2408
|
+
v.to_s
|
2409
|
+
end
|
2410
|
+
end
|
2411
|
+
|
2412
|
+
# Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
|
2413
|
+
# outside PostgreSQL bigint range, so PostgreSQL doesn't treat
|
2414
|
+
# the value as numeric.
|
2415
|
+
def literal_integer_outside_bigint_range(v)
|
2416
|
+
raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
|
2417
|
+
end
|
2418
|
+
|
2049
2419
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
2050
2420
|
def literal_string_append(sql, v)
|
2051
2421
|
sql << "'" << v.gsub("'", "''") << "'"
|
@@ -2141,6 +2511,41 @@ module Sequel
|
|
2141
2511
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
2142
2512
|
end
|
2143
2513
|
|
2514
|
+
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
2515
|
+
def select_with_sql_cte(sql, cte)
|
2516
|
+
super
|
2517
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
2518
|
+
end
|
2519
|
+
|
2520
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
2521
|
+
if search_opts = cte[:search]
|
2522
|
+
sql << if search_opts[:type] == :breadth
|
2523
|
+
" SEARCH BREADTH FIRST BY "
|
2524
|
+
else
|
2525
|
+
" SEARCH DEPTH FIRST BY "
|
2526
|
+
end
|
2527
|
+
|
2528
|
+
identifier_list_append(sql, Array(search_opts[:by]))
|
2529
|
+
sql << " SET "
|
2530
|
+
identifier_append(sql, search_opts[:set] || :ordercol)
|
2531
|
+
end
|
2532
|
+
|
2533
|
+
if cycle_opts = cte[:cycle]
|
2534
|
+
sql << " CYCLE "
|
2535
|
+
identifier_list_append(sql, Array(cycle_opts[:columns]))
|
2536
|
+
sql << " SET "
|
2537
|
+
identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
|
2538
|
+
if cycle_opts.has_key?(:cycle_value)
|
2539
|
+
sql << " TO "
|
2540
|
+
literal_append(sql, cycle_opts[:cycle_value])
|
2541
|
+
sql << " DEFAULT "
|
2542
|
+
literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
|
2543
|
+
end
|
2544
|
+
sql << " USING "
|
2545
|
+
identifier_append(sql, cycle_opts[:path_column] || :path)
|
2546
|
+
end
|
2547
|
+
end
|
2548
|
+
|
2144
2549
|
# The version of the database server
|
2145
2550
|
def server_version
|
2146
2551
|
db.server_version(@opts[:server])
|