sequel 5.39.0 → 5.72.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 +408 -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 +13 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +26 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- 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/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +28 -16
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +90 -9
- data/lib/sequel/adapters/shared/mysql.rb +47 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -1
- data/lib/sequel/adapters/shared/postgres.rb +496 -178
- data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
- data/lib/sequel/adapters/shared/sqlite.rb +116 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -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 +55 -31
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +46 -53
- data/lib/sequel/database/schema_methods.rb +18 -2
- data/lib/sequel/dataset/actions.rb +108 -14
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +171 -44
- data/lib/sequel/dataset/sql.rb +182 -47
- 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 +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +439 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- 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 +71 -31
- 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 +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +9 -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 +11 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +32 -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 +2 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- 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 +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +11 -24
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +21 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- 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/model/associations.rb +345 -101
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +10 -6
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- 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 +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- 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 +12 -7
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -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 +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- 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 +46 -12
- 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 +132 -38
@@ -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)
|
@@ -549,63 +559,12 @@ module Sequel
|
|
549
559
|
def foreign_key_list(table, opts=OPTS)
|
550
560
|
m = output_identifier_meth
|
551
561
|
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
562
|
|
605
563
|
h = {}
|
606
564
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
565
|
+
reverse = opts[:reverse]
|
607
566
|
|
608
|
-
|
567
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
609
568
|
if reverse
|
610
569
|
key = [row[:schema], row[:table], row[:name]]
|
611
570
|
else
|
@@ -640,6 +599,14 @@ module Sequel
|
|
640
599
|
def freeze
|
641
600
|
server_version
|
642
601
|
supports_prepared_transactions?
|
602
|
+
_schema_ds
|
603
|
+
_select_serial_sequence_ds
|
604
|
+
_select_custom_sequence_ds
|
605
|
+
_select_pk_ds
|
606
|
+
_indexes_ds
|
607
|
+
_check_constraints_ds
|
608
|
+
_foreign_key_list_ds
|
609
|
+
_reverse_foreign_key_list_ds
|
643
610
|
@conversion_procs.freeze
|
644
611
|
super
|
645
612
|
end
|
@@ -647,38 +614,11 @@ module Sequel
|
|
647
614
|
# Use the pg_* system tables to determine indexes on a table
|
648
615
|
def indexes(table, opts=OPTS)
|
649
616
|
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
|
617
|
+
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
618
|
+
cond[:indpred] = nil unless opts[:include_partial]
|
679
619
|
|
680
620
|
indexes = {}
|
681
|
-
|
621
|
+
_indexes_ds.where_each(cond) do |r|
|
682
622
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
683
623
|
i[:columns] << m.call(r[:column])
|
684
624
|
end
|
@@ -711,8 +651,7 @@ module Sequel
|
|
711
651
|
def primary_key(table, opts=OPTS)
|
712
652
|
quoted_table = quote_schema_table(table)
|
713
653
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
714
|
-
|
715
|
-
value = fetch(sql).single_value
|
654
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
716
655
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
717
656
|
end
|
718
657
|
|
@@ -720,24 +659,21 @@ module Sequel
|
|
720
659
|
def primary_key_sequence(table, opts=OPTS)
|
721
660
|
quoted_table = quote_schema_table(table)
|
722
661
|
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
|
662
|
+
cond = {Sequel[:t][:oid] => regclass_oid(table, opts)}
|
663
|
+
value = if pks = _select_serial_sequence_ds.first(cond)
|
664
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
|
665
|
+
elsif pks = _select_custom_sequence_ds.first(cond)
|
666
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
733
667
|
end
|
668
|
+
|
669
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
734
670
|
end
|
735
671
|
|
736
672
|
# Refresh the materialized view with the given name.
|
737
673
|
#
|
738
674
|
# DB.refresh_view(:items_view)
|
739
675
|
# # REFRESH MATERIALIZED VIEW items_view
|
740
|
-
# DB.refresh_view(:items_view, :
|
676
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
741
677
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
742
678
|
def refresh_view(name, opts=OPTS)
|
743
679
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
@@ -756,10 +692,12 @@ module Sequel
|
|
756
692
|
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
757
693
|
increment_by = :seqincrement
|
758
694
|
min_value = :seqmin
|
695
|
+
# :nocov:
|
759
696
|
else
|
760
697
|
seq_ds = metadata_dataset.from(LiteralString.new(seq))
|
761
698
|
increment_by = :increment_by
|
762
699
|
min_value = :min_value
|
700
|
+
# :nocov:
|
763
701
|
end
|
764
702
|
|
765
703
|
get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
|
@@ -772,7 +710,9 @@ module Sequel
|
|
772
710
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
773
711
|
# managing incrementing primary keys.
|
774
712
|
def serial_primary_key_options
|
713
|
+
# :nocov:
|
775
714
|
auto_increment_key = server_version >= 100002 ? :identity : :serial
|
715
|
+
# :nocov:
|
776
716
|
{:primary_key => true, auto_increment_key => true, :type=>Integer}
|
777
717
|
end
|
778
718
|
|
@@ -865,6 +805,7 @@ module Sequel
|
|
865
805
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
866
806
|
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
867
807
|
def values(v)
|
808
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
868
809
|
@default_dataset.clone(:values=>v)
|
869
810
|
end
|
870
811
|
|
@@ -883,6 +824,220 @@ module Sequel
|
|
883
824
|
|
884
825
|
private
|
885
826
|
|
827
|
+
# Dataset used to retrieve CHECK constraint information
|
828
|
+
def _check_constraints_ds
|
829
|
+
@_check_constraints_ds ||= metadata_dataset.
|
830
|
+
from{pg_constraint.as(:co)}.
|
831
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
832
|
+
where(:contype=>'c').
|
833
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
834
|
+
end
|
835
|
+
|
836
|
+
# Dataset used to retrieve foreign keys referenced by a table
|
837
|
+
def _foreign_key_list_ds
|
838
|
+
@_foreign_key_list_ds ||= __foreign_key_list_ds(false)
|
839
|
+
end
|
840
|
+
|
841
|
+
# Dataset used to retrieve foreign keys referencing a table
|
842
|
+
def _reverse_foreign_key_list_ds
|
843
|
+
@_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
|
844
|
+
end
|
845
|
+
|
846
|
+
# Build dataset used for foreign key list methods.
|
847
|
+
def __foreign_key_list_ds(reverse)
|
848
|
+
if reverse
|
849
|
+
ctable = Sequel[:att2]
|
850
|
+
cclass = Sequel[:cl2]
|
851
|
+
rtable = Sequel[:att]
|
852
|
+
rclass = Sequel[:cl]
|
853
|
+
else
|
854
|
+
ctable = Sequel[:att]
|
855
|
+
cclass = Sequel[:cl]
|
856
|
+
rtable = Sequel[:att2]
|
857
|
+
rclass = Sequel[:cl2]
|
858
|
+
end
|
859
|
+
|
860
|
+
if server_version >= 90500
|
861
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
862
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
863
|
+
# :nocov:
|
864
|
+
else
|
865
|
+
range = 0...32
|
866
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
867
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
868
|
+
# :nocov:
|
869
|
+
end
|
870
|
+
|
871
|
+
ds = metadata_dataset.
|
872
|
+
from{pg_constraint.as(:co)}.
|
873
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
874
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
875
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
876
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
877
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
878
|
+
order{[co[:conname], cpos]}.
|
879
|
+
where{{
|
880
|
+
cl[:relkind]=>%w'r p',
|
881
|
+
co[:contype]=>'f',
|
882
|
+
cpos=>rpos
|
883
|
+
}}.
|
884
|
+
select{[
|
885
|
+
co[:conname].as(:name),
|
886
|
+
ctable[:attname].as(:column),
|
887
|
+
co[:confupdtype].as(:on_update),
|
888
|
+
co[:confdeltype].as(:on_delete),
|
889
|
+
cl2[:relname].as(:table),
|
890
|
+
rtable[:attname].as(:refcolumn),
|
891
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
892
|
+
nsp[:nspname].as(:schema)
|
893
|
+
]}
|
894
|
+
|
895
|
+
if reverse
|
896
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
897
|
+
end
|
898
|
+
|
899
|
+
ds
|
900
|
+
end
|
901
|
+
|
902
|
+
# Dataset used to retrieve index information
|
903
|
+
def _indexes_ds
|
904
|
+
@_indexes_ds ||= begin
|
905
|
+
if server_version >= 90500
|
906
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
907
|
+
# :nocov:
|
908
|
+
else
|
909
|
+
range = 0...32
|
910
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
911
|
+
# :nocov:
|
912
|
+
end
|
913
|
+
|
914
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
915
|
+
|
916
|
+
ds = metadata_dataset.
|
917
|
+
from{pg_class.as(:tab)}.
|
918
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
919
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
920
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
921
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
922
|
+
where{{
|
923
|
+
indc[:relkind]=>%w'i I',
|
924
|
+
ind[:indisprimary]=>false,
|
925
|
+
:indexprs=>nil,
|
926
|
+
:indisvalid=>true}}.
|
927
|
+
order(*order).
|
928
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
929
|
+
|
930
|
+
# :nocov:
|
931
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
932
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
933
|
+
# :nocov:
|
934
|
+
|
935
|
+
ds
|
936
|
+
end
|
937
|
+
end
|
938
|
+
|
939
|
+
# Dataset used to determine custom serial sequences for tables
|
940
|
+
def _select_custom_sequence_ds
|
941
|
+
@_select_custom_sequence_ds ||= metadata_dataset.
|
942
|
+
from{pg_class.as(:t)}.
|
943
|
+
join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
|
944
|
+
join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
|
945
|
+
join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
|
946
|
+
join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
|
947
|
+
where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
|
948
|
+
select{
|
949
|
+
expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
|
950
|
+
[
|
951
|
+
name[:nspname].as(:schema),
|
952
|
+
Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
|
953
|
+
]
|
954
|
+
}
|
955
|
+
end
|
956
|
+
|
957
|
+
# Dataset used to determine normal serial sequences for tables
|
958
|
+
def _select_serial_sequence_ds
|
959
|
+
@_serial_sequence_ds ||= metadata_dataset.
|
960
|
+
from{[
|
961
|
+
pg_class.as(:seq),
|
962
|
+
pg_attribute.as(:attr),
|
963
|
+
pg_depend.as(:dep),
|
964
|
+
pg_namespace.as(:name),
|
965
|
+
pg_constraint.as(:cons),
|
966
|
+
pg_class.as(:t)
|
967
|
+
]}.
|
968
|
+
where{[
|
969
|
+
[seq[:oid], dep[:objid]],
|
970
|
+
[seq[:relnamespace], name[:oid]],
|
971
|
+
[seq[:relkind], 'S'],
|
972
|
+
[attr[:attrelid], dep[:refobjid]],
|
973
|
+
[attr[:attnum], dep[:refobjsubid]],
|
974
|
+
[attr[:attrelid], cons[:conrelid]],
|
975
|
+
[attr[:attnum], cons[:conkey].sql_subscript(1)],
|
976
|
+
[attr[:attrelid], t[:oid]],
|
977
|
+
[cons[:contype], 'p']
|
978
|
+
]}.
|
979
|
+
select{[
|
980
|
+
name[:nspname].as(:schema),
|
981
|
+
seq[:relname].as(:sequence)
|
982
|
+
]}
|
983
|
+
end
|
984
|
+
|
985
|
+
# Dataset used to determine primary keys for tables
|
986
|
+
def _select_pk_ds
|
987
|
+
@_select_pk_ds ||= metadata_dataset.
|
988
|
+
from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
|
989
|
+
where{[
|
990
|
+
[pg_class[:oid], pg_attribute[:attrelid]],
|
991
|
+
[pg_class[:relnamespace], pg_namespace[:oid]],
|
992
|
+
[pg_class[:oid], pg_index[:indrelid]],
|
993
|
+
[pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
|
994
|
+
[pg_index[:indisprimary], 't']
|
995
|
+
]}.
|
996
|
+
select{pg_attribute[:attname].as(:pk)}
|
997
|
+
end
|
998
|
+
|
999
|
+
# Dataset used to get schema for tables
|
1000
|
+
def _schema_ds
|
1001
|
+
@_schema_ds ||= begin
|
1002
|
+
ds = metadata_dataset.select{[
|
1003
|
+
pg_attribute[:attname].as(:name),
|
1004
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1005
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1006
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1007
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1008
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1009
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1010
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key),
|
1011
|
+
Sequel[:pg_type][:typtype],
|
1012
|
+
(~Sequel[Sequel[:elementtype][:oid]=>nil]).as(:is_array),
|
1013
|
+
]}.
|
1014
|
+
from(:pg_class).
|
1015
|
+
join(:pg_attribute, :attrelid=>:oid).
|
1016
|
+
join(:pg_type, :oid=>:atttypid).
|
1017
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1018
|
+
left_outer_join(Sequel[:pg_type].as(:elementtype), :typarray=>Sequel[:pg_type][:oid]).
|
1019
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1020
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1021
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
1022
|
+
where{pg_attribute[:attnum] > 0}.
|
1023
|
+
order{pg_attribute[:attnum]}
|
1024
|
+
|
1025
|
+
# :nocov:
|
1026
|
+
if server_version > 100000
|
1027
|
+
# :nocov:
|
1028
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
1029
|
+
|
1030
|
+
# :nocov:
|
1031
|
+
if server_version > 120000
|
1032
|
+
# :nocov:
|
1033
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
ds
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
886
1041
|
def alter_table_add_column_sql(table, op)
|
887
1042
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
888
1043
|
end
|
@@ -1116,6 +1271,7 @@ module Sequel
|
|
1116
1271
|
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
1117
1272
|
#{'STRICT' if opts[:strict]}
|
1118
1273
|
#{'SECURITY DEFINER' if opts[:security_definer]}
|
1274
|
+
#{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
|
1119
1275
|
#{"COST #{opts[:cost]}" if opts[:cost]}
|
1120
1276
|
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
1121
1277
|
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
@@ -1149,7 +1305,7 @@ module Sequel
|
|
1149
1305
|
when :hash
|
1150
1306
|
mod, remainder = generator.hash_values
|
1151
1307
|
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
1152
|
-
when :default
|
1308
|
+
else # when :default
|
1153
1309
|
sql << " DEFAULT"
|
1154
1310
|
end
|
1155
1311
|
|
@@ -1237,13 +1393,17 @@ module Sequel
|
|
1237
1393
|
raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
|
1238
1394
|
filter = " WHEN #{filter_expr(filter)}"
|
1239
1395
|
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(', ')})"
|
1396
|
+
"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
1397
|
end
|
1242
1398
|
|
1243
1399
|
# DDL fragment for initial part of CREATE VIEW statement
|
1244
1400
|
def create_view_prefix_sql(name, options)
|
1245
1401
|
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
1402
|
|
1403
|
+
if options[:security_invoker]
|
1404
|
+
sql += " WITH (security_invoker)"
|
1405
|
+
end
|
1406
|
+
|
1247
1407
|
if tablespace = options[:tablespace]
|
1248
1408
|
sql += " TABLESPACE #{quote_identifier(tablespace)}"
|
1249
1409
|
end
|
@@ -1301,16 +1461,20 @@ module Sequel
|
|
1301
1461
|
def index_definition_sql(table_name, index)
|
1302
1462
|
cols = index[:columns]
|
1303
1463
|
index_name = index[:name] || default_index_name(table_name, cols)
|
1464
|
+
|
1304
1465
|
expr = if o = index[:opclass]
|
1305
1466
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
1306
1467
|
else
|
1307
1468
|
literal(Array(cols))
|
1308
1469
|
end
|
1470
|
+
|
1309
1471
|
if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
|
1310
1472
|
unique = "UNIQUE " if index[:unique]
|
1311
1473
|
index_type = index[:type]
|
1312
1474
|
filter = index[:where] || index[:filter]
|
1313
1475
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
1476
|
+
nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
|
1477
|
+
|
1314
1478
|
case index_type
|
1315
1479
|
when :full_text
|
1316
1480
|
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
@@ -1318,7 +1482,8 @@ module Sequel
|
|
1318
1482
|
when :spatial
|
1319
1483
|
index_type = :gist
|
1320
1484
|
end
|
1321
|
-
|
1485
|
+
|
1486
|
+
"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
1487
|
end
|
1323
1488
|
|
1324
1489
|
# Setup datastructures shared by all postgres adapters.
|
@@ -1335,7 +1500,7 @@ module Sequel
|
|
1335
1500
|
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
1336
1501
|
ds = filter_schema(ds, opts)
|
1337
1502
|
m = output_identifier_meth
|
1338
|
-
if
|
1503
|
+
if defined?(yield)
|
1339
1504
|
yield(ds)
|
1340
1505
|
elsif opts[:qualify]
|
1341
1506
|
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
@@ -1344,11 +1509,6 @@ module Sequel
|
|
1344
1509
|
end
|
1345
1510
|
end
|
1346
1511
|
|
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
1512
|
# Return an expression the oid for the table expr. Used by the metadata parsing
|
1353
1513
|
# code to disambiguate unqualified tables.
|
1354
1514
|
def regclass_oid(expr, opts=OPTS)
|
@@ -1382,11 +1542,12 @@ module Sequel
|
|
1382
1542
|
end
|
1383
1543
|
|
1384
1544
|
# 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
|
1545
|
+
# a rename table operation, so specifying a new schema in new_name will not have an effect.
|
1386
1546
|
def rename_table_sql(name, new_name)
|
1387
1547
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
1388
1548
|
end
|
1389
1549
|
|
1550
|
+
# Handle interval and citext types.
|
1390
1551
|
def schema_column_type(db_type)
|
1391
1552
|
case db_type
|
1392
1553
|
when /\Ainterval\z/io
|
@@ -1398,39 +1559,48 @@ module Sequel
|
|
1398
1559
|
end
|
1399
1560
|
end
|
1400
1561
|
|
1562
|
+
# The schema :type entry to use for array types.
|
1563
|
+
def schema_array_type(db_type)
|
1564
|
+
:array
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
# The schema :type entry to use for row/composite types.
|
1568
|
+
def schema_composite_type(db_type)
|
1569
|
+
:composite
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
# The schema :type entry to use for enum types.
|
1573
|
+
def schema_enum_type(db_type)
|
1574
|
+
:enum
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
# The schema :type entry to use for range types.
|
1578
|
+
def schema_range_type(db_type)
|
1579
|
+
:range
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
# The schema :type entry to use for multirange types.
|
1583
|
+
def schema_multirange_type(db_type)
|
1584
|
+
:multirange
|
1585
|
+
end
|
1586
|
+
|
1587
|
+
MIN_DATE = Date.new(-4713, 11, 24)
|
1588
|
+
MAX_DATE = Date.new(5874897, 12, 31)
|
1589
|
+
MIN_TIMESTAMP = Time.utc(-4713, 11, 24).freeze
|
1590
|
+
MAX_TIMESTAMP = (Time.utc(294277) - Rational(1, 1000000)).freeze
|
1591
|
+
TYPTYPE_METHOD_MAP = {
|
1592
|
+
'c' => :schema_composite_type,
|
1593
|
+
'e' => :schema_enum_type,
|
1594
|
+
'r' => :schema_range_type,
|
1595
|
+
'm' => :schema_multirange_type,
|
1596
|
+
}
|
1597
|
+
TYPTYPE_METHOD_MAP.default = :schema_column_type
|
1598
|
+
TYPTYPE_METHOD_MAP.freeze
|
1401
1599
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
1402
1600
|
def schema_parse_table(table_name, opts)
|
1403
1601
|
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
1602
|
|
1433
|
-
|
1603
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
1434
1604
|
row[:default] = nil if blank_object?(row[:default])
|
1435
1605
|
if row[:base_oid]
|
1436
1606
|
row[:domain_oid] = row[:oid]
|
@@ -1441,11 +1611,33 @@ module Sequel
|
|
1441
1611
|
row.delete(:base_oid)
|
1442
1612
|
row.delete(:db_base_type)
|
1443
1613
|
end
|
1444
|
-
|
1614
|
+
|
1615
|
+
db_type = row[:db_type]
|
1616
|
+
row[:type] = if row.delete(:is_array)
|
1617
|
+
schema_array_type(db_type)
|
1618
|
+
else
|
1619
|
+
send(TYPTYPE_METHOD_MAP[row.delete(:typtype)], db_type)
|
1620
|
+
end
|
1445
1621
|
identity = row.delete(:attidentity)
|
1446
1622
|
if row[:primary_key]
|
1447
1623
|
row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
|
1448
1624
|
end
|
1625
|
+
|
1626
|
+
# :nocov:
|
1627
|
+
if server_version >= 90600
|
1628
|
+
# :nocov:
|
1629
|
+
case row[:oid]
|
1630
|
+
when 1082
|
1631
|
+
row[:min_value] = MIN_DATE
|
1632
|
+
row[:max_value] = MAX_DATE
|
1633
|
+
when 1184, 1114
|
1634
|
+
if Sequel.datetime_class == Time
|
1635
|
+
row[:min_value] = MIN_TIMESTAMP
|
1636
|
+
row[:max_value] = MAX_TIMESTAMP
|
1637
|
+
end
|
1638
|
+
end
|
1639
|
+
end
|
1640
|
+
|
1449
1641
|
[m.call(row.delete(:name)), row]
|
1450
1642
|
end
|
1451
1643
|
end
|
@@ -1500,10 +1692,12 @@ module Sequel
|
|
1500
1692
|
# disallowed or there is a size specified, use the varchar type.
|
1501
1693
|
# Otherwise use the text type.
|
1502
1694
|
def type_literal_generic_string(column)
|
1503
|
-
if column[:
|
1504
|
-
|
1505
|
-
elsif column[:
|
1506
|
-
"
|
1695
|
+
if column[:text]
|
1696
|
+
:text
|
1697
|
+
elsif column[:fixed]
|
1698
|
+
"char(#{column[:size]||default_string_column_size})"
|
1699
|
+
elsif column[:text] == false || column[:size]
|
1700
|
+
"varchar(#{column[:size]||default_string_column_size})"
|
1507
1701
|
else
|
1508
1702
|
:text
|
1509
1703
|
end
|
@@ -1511,7 +1705,9 @@ module Sequel
|
|
1511
1705
|
|
1512
1706
|
# PostgreSQL 9.4+ supports views with check option.
|
1513
1707
|
def view_with_check_option_support
|
1708
|
+
# :nocov:
|
1514
1709
|
:local if server_version >= 90400
|
1710
|
+
# :nocov:
|
1515
1711
|
end
|
1516
1712
|
end
|
1517
1713
|
|
@@ -1522,7 +1718,7 @@ module Sequel
|
|
1522
1718
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
1523
1719
|
|
1524
1720
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1525
|
-
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']])
|
1721
|
+
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']])
|
1526
1722
|
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']])
|
1527
1723
|
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']])
|
1528
1724
|
|
@@ -1549,8 +1745,6 @@ module Sequel
|
|
1549
1745
|
literal_append(sql, args[0])
|
1550
1746
|
sql << ' ' << op.to_s << ' '
|
1551
1747
|
literal_append(sql, args[1])
|
1552
|
-
sql << " ESCAPE "
|
1553
|
-
literal_append(sql, "\\")
|
1554
1748
|
sql << ')'
|
1555
1749
|
else
|
1556
1750
|
super
|
@@ -1575,6 +1769,12 @@ module Sequel
|
|
1575
1769
|
clone(:disable_insert_returning=>true)
|
1576
1770
|
end
|
1577
1771
|
|
1772
|
+
# Always return false when using VALUES
|
1773
|
+
def empty?
|
1774
|
+
return false if @opts[:values]
|
1775
|
+
super
|
1776
|
+
end
|
1777
|
+
|
1578
1778
|
# Return the results of an EXPLAIN query as a string
|
1579
1779
|
def explain(opts=OPTS)
|
1580
1780
|
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
@@ -1725,13 +1925,22 @@ module Sequel
|
|
1725
1925
|
ds.insert_sql(*values)
|
1726
1926
|
end
|
1727
1927
|
|
1928
|
+
# Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
|
1929
|
+
# USING columns.
|
1930
|
+
def join_table(type, table, expr=nil, options=OPTS, &block)
|
1931
|
+
if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
|
1932
|
+
options = options.merge(:join_using=>true)
|
1933
|
+
end
|
1934
|
+
super
|
1935
|
+
end
|
1936
|
+
|
1728
1937
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
1729
1938
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
1730
1939
|
# a new transaction, locks the table, and yields. If a block is not given,
|
1731
1940
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
1732
1941
|
# if you lock the table outside of an existing transaction. Returns nil.
|
1733
1942
|
def lock(mode, opts=OPTS)
|
1734
|
-
if
|
1943
|
+
if defined?(yield) # perform locking inside a transaction and yield to block
|
1735
1944
|
@db.transaction(opts){lock(mode, opts); yield}
|
1736
1945
|
else
|
1737
1946
|
sql = 'LOCK TABLE '.dup
|
@@ -1746,6 +1955,41 @@ module Sequel
|
|
1746
1955
|
nil
|
1747
1956
|
end
|
1748
1957
|
|
1958
|
+
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
1959
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1960
|
+
# use it as additional conditions for the match.
|
1961
|
+
#
|
1962
|
+
# merge_do_nothing_when_matched
|
1963
|
+
# # WHEN MATCHED THEN DO NOTHING
|
1964
|
+
#
|
1965
|
+
# merge_do_nothing_when_matched{a > 30}
|
1966
|
+
# # WHEN MATCHED AND (a > 30) THEN DO NOTHING
|
1967
|
+
def merge_do_nothing_when_matched(&block)
|
1968
|
+
_merge_when(:type=>:matched, &block)
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
# Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
|
1972
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1973
|
+
# use it as additional conditions for the match.
|
1974
|
+
#
|
1975
|
+
# merge_do_nothing_when_not_matched
|
1976
|
+
# # WHEN NOT MATCHED THEN DO NOTHING
|
1977
|
+
#
|
1978
|
+
# merge_do_nothing_when_not_matched{a > 30}
|
1979
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
|
1980
|
+
def merge_do_nothing_when_not_matched(&block)
|
1981
|
+
_merge_when(:type=>:not_matched, &block)
|
1982
|
+
end
|
1983
|
+
|
1984
|
+
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
1985
|
+
def merge_insert(*values, &block)
|
1986
|
+
h = {:type=>:insert, :values=>values}
|
1987
|
+
if override = @opts[:override]
|
1988
|
+
h[:override] = insert_override_sql(String.new)
|
1989
|
+
end
|
1990
|
+
_merge_when(h, &block)
|
1991
|
+
end
|
1992
|
+
|
1749
1993
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
1750
1994
|
# always use the user supplied value, and an error is not raised for identity
|
1751
1995
|
# columns that are GENERATED ALWAYS.
|
@@ -1813,6 +2057,11 @@ module Sequel
|
|
1813
2057
|
true
|
1814
2058
|
end
|
1815
2059
|
|
2060
|
+
# PostgreSQL 15+ supports MERGE.
|
2061
|
+
def supports_merge?
|
2062
|
+
server_version >= 150000
|
2063
|
+
end
|
2064
|
+
|
1816
2065
|
# PostgreSQL supports NOWAIT.
|
1817
2066
|
def supports_nowait?
|
1818
2067
|
true
|
@@ -1858,6 +2107,8 @@ module Sequel
|
|
1858
2107
|
server_version >= 90000
|
1859
2108
|
when :groups, :exclude
|
1860
2109
|
server_version >= 110000
|
2110
|
+
else
|
2111
|
+
false
|
1861
2112
|
end
|
1862
2113
|
end
|
1863
2114
|
|
@@ -1900,12 +2151,10 @@ module Sequel
|
|
1900
2151
|
# Otherwise, return an array of hashes.
|
1901
2152
|
def _import(columns, values, opts=OPTS)
|
1902
2153
|
if @opts[:returning]
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
statements.map{|st| returning_fetch_rows(st)}
|
1908
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
2154
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
2155
|
+
# that there's only ever a single statement.
|
2156
|
+
sql = multi_insert_sql(columns, values)[0]
|
2157
|
+
returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
|
1909
2158
|
elsif opts[:return] == :primary_key
|
1910
2159
|
returning(insert_pk)._import(columns, values, opts)
|
1911
2160
|
else
|
@@ -1923,12 +2172,33 @@ module Sequel
|
|
1923
2172
|
|
1924
2173
|
private
|
1925
2174
|
|
2175
|
+
# Append the INSERT sql used in a MERGE
|
2176
|
+
def _merge_insert_sql(sql, data)
|
2177
|
+
sql << " THEN INSERT "
|
2178
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
2179
|
+
_insert_columns_sql(sql, columns)
|
2180
|
+
if override = data[:override]
|
2181
|
+
sql << override
|
2182
|
+
end
|
2183
|
+
_insert_values_sql(sql, values)
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
def _merge_matched_sql(sql, data)
|
2187
|
+
sql << " THEN DO NOTHING"
|
2188
|
+
end
|
2189
|
+
alias _merge_not_matched_sql _merge_matched_sql
|
2190
|
+
|
1926
2191
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
1927
2192
|
def _truncate_sql(table)
|
1928
2193
|
to = @opts[:truncate_opts] || OPTS
|
1929
2194
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
1930
2195
|
end
|
1931
2196
|
|
2197
|
+
# Use from_self for aggregate dataset using VALUES.
|
2198
|
+
def aggreate_dataset_use_from_self?
|
2199
|
+
super || @opts[:values]
|
2200
|
+
end
|
2201
|
+
|
1932
2202
|
# Allow truncation of multiple source tables.
|
1933
2203
|
def check_truncation_allowed!
|
1934
2204
|
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
@@ -1988,25 +2258,23 @@ module Sequel
|
|
1988
2258
|
|
1989
2259
|
# Return the primary key to use for RETURNING in an INSERT statement
|
1990
2260
|
def insert_pk
|
1991
|
-
|
1992
|
-
|
1993
|
-
|
1994
|
-
|
1995
|
-
|
1996
|
-
end
|
2261
|
+
(f = opts[:from]) && !f.empty? && (t = f.first)
|
2262
|
+
case t
|
2263
|
+
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
2264
|
+
if pk = db.primary_key(t)
|
2265
|
+
Sequel::SQL::Identifier.new(pk)
|
1997
2266
|
end
|
1998
2267
|
end
|
1999
2268
|
end
|
2000
2269
|
|
2001
2270
|
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
2002
|
-
def
|
2271
|
+
def insert_override_sql(sql)
|
2003
2272
|
case opts[:override]
|
2004
2273
|
when :system
|
2005
2274
|
sql << " OVERRIDING SYSTEM VALUE"
|
2006
2275
|
when :user
|
2007
2276
|
sql << " OVERRIDING USER VALUE"
|
2008
2277
|
end
|
2009
|
-
super
|
2010
2278
|
end
|
2011
2279
|
|
2012
2280
|
# For multiple table support, PostgreSQL requires at least
|
@@ -2021,6 +2289,17 @@ module Sequel
|
|
2021
2289
|
end
|
2022
2290
|
end
|
2023
2291
|
|
2292
|
+
# Support table aliases for USING columns
|
2293
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
2294
|
+
if using_columns.is_a?(SQL::AliasedExpression)
|
2295
|
+
super(sql, using_columns.expression)
|
2296
|
+
sql << ' AS '
|
2297
|
+
identifier_append(sql, using_columns.alias)
|
2298
|
+
else
|
2299
|
+
super
|
2300
|
+
end
|
2301
|
+
end
|
2302
|
+
|
2024
2303
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
2025
2304
|
def literal_blob_append(sql, v)
|
2026
2305
|
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
@@ -2044,6 +2323,22 @@ module Sequel
|
|
2044
2323
|
end
|
2045
2324
|
end
|
2046
2325
|
|
2326
|
+
# Handle Ruby integers outside PostgreSQL bigint range specially.
|
2327
|
+
def literal_integer(v)
|
2328
|
+
if v > 9223372036854775807 || v < -9223372036854775808
|
2329
|
+
literal_integer_outside_bigint_range(v)
|
2330
|
+
else
|
2331
|
+
v.to_s
|
2332
|
+
end
|
2333
|
+
end
|
2334
|
+
|
2335
|
+
# Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
|
2336
|
+
# outside PostgreSQL bigint range, so PostgreSQL doesn't treat
|
2337
|
+
# the value as numeric.
|
2338
|
+
def literal_integer_outside_bigint_range(v)
|
2339
|
+
raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
|
2340
|
+
end
|
2341
|
+
|
2047
2342
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
2048
2343
|
def literal_string_append(sql, v)
|
2049
2344
|
sql << "'" << v.gsub("'", "''") << "'"
|
@@ -2139,15 +2434,38 @@ module Sequel
|
|
2139
2434
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
2140
2435
|
end
|
2141
2436
|
|
2142
|
-
# Support
|
2143
|
-
def
|
2437
|
+
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
2438
|
+
def select_with_sql_cte(sql, cte)
|
2144
2439
|
super
|
2440
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
2441
|
+
end
|
2442
|
+
|
2443
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
2444
|
+
if search_opts = cte[:search]
|
2445
|
+
sql << if search_opts[:type] == :breadth
|
2446
|
+
" SEARCH BREADTH FIRST BY "
|
2447
|
+
else
|
2448
|
+
" SEARCH DEPTH FIRST BY "
|
2449
|
+
end
|
2145
2450
|
|
2146
|
-
|
2147
|
-
|
2148
|
-
sql
|
2149
|
-
|
2150
|
-
|
2451
|
+
identifier_list_append(sql, Array(search_opts[:by]))
|
2452
|
+
sql << " SET "
|
2453
|
+
identifier_append(sql, search_opts[:set] || :ordercol)
|
2454
|
+
end
|
2455
|
+
|
2456
|
+
if cycle_opts = cte[:cycle]
|
2457
|
+
sql << " CYCLE "
|
2458
|
+
identifier_list_append(sql, Array(cycle_opts[:columns]))
|
2459
|
+
sql << " SET "
|
2460
|
+
identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
|
2461
|
+
if cycle_opts.has_key?(:cycle_value)
|
2462
|
+
sql << " TO "
|
2463
|
+
literal_append(sql, cycle_opts[:cycle_value])
|
2464
|
+
sql << " DEFAULT "
|
2465
|
+
literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
|
2466
|
+
end
|
2467
|
+
sql << " USING "
|
2468
|
+
identifier_append(sql, cycle_opts[:path_column] || :path)
|
2151
2469
|
end
|
2152
2470
|
end
|
2153
2471
|
|