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