sequel 5.58.0 → 5.78.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 +288 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +24 -23
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +20 -12
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- 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/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +62 -16
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +80 -1
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +494 -164
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +40 -4
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- 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/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/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/database/schema_methods.rb +13 -3
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +62 -37
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -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 +21 -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/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- 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 +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- 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 +50 -11
- data/lib/sequel/model/base.rb +45 -21
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/composition.rb +2 -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/finder.rb +4 -2
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- 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_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- 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/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +5 -5
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +1 -1
- metadata +76 -6
@@ -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).
|
@@ -490,6 +498,25 @@ module Sequel
|
|
490
498
|
:postgres
|
491
499
|
end
|
492
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
|
+
|
493
520
|
# Use PostgreSQL's DO syntax to execute an anonymous code block. The code should
|
494
521
|
# be the literal code string to use in the underlying procedural language. Options:
|
495
522
|
#
|
@@ -551,63 +578,12 @@ module Sequel
|
|
551
578
|
def foreign_key_list(table, opts=OPTS)
|
552
579
|
m = output_identifier_meth
|
553
580
|
schema, _ = opts.fetch(:schema, schema_and_table(table))
|
554
|
-
oid = regclass_oid(table)
|
555
|
-
reverse = opts[:reverse]
|
556
|
-
|
557
|
-
if reverse
|
558
|
-
ctable = Sequel[:att2]
|
559
|
-
cclass = Sequel[:cl2]
|
560
|
-
rtable = Sequel[:att]
|
561
|
-
rclass = Sequel[:cl]
|
562
|
-
else
|
563
|
-
ctable = Sequel[:att]
|
564
|
-
cclass = Sequel[:cl]
|
565
|
-
rtable = Sequel[:att2]
|
566
|
-
rclass = Sequel[:cl2]
|
567
|
-
end
|
568
|
-
|
569
|
-
if server_version >= 90500
|
570
|
-
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
571
|
-
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
572
|
-
else
|
573
|
-
range = 0...32
|
574
|
-
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
575
|
-
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
576
|
-
end
|
577
|
-
|
578
|
-
ds = metadata_dataset.
|
579
|
-
from{pg_constraint.as(:co)}.
|
580
|
-
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
581
|
-
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
582
|
-
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
583
|
-
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
584
|
-
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
585
|
-
order{[co[:conname], cpos]}.
|
586
|
-
where{{
|
587
|
-
cl[:relkind]=>'r',
|
588
|
-
co[:contype]=>'f',
|
589
|
-
cl[:oid]=>oid,
|
590
|
-
cpos=>rpos
|
591
|
-
}}.
|
592
|
-
select{[
|
593
|
-
co[:conname].as(:name),
|
594
|
-
ctable[:attname].as(:column),
|
595
|
-
co[:confupdtype].as(:on_update),
|
596
|
-
co[:confdeltype].as(:on_delete),
|
597
|
-
cl2[:relname].as(:table),
|
598
|
-
rtable[:attname].as(:refcolumn),
|
599
|
-
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
600
|
-
nsp[:nspname].as(:schema)
|
601
|
-
]}
|
602
|
-
|
603
|
-
if reverse
|
604
|
-
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
605
|
-
end
|
606
581
|
|
607
582
|
h = {}
|
608
583
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
584
|
+
reverse = opts[:reverse]
|
609
585
|
|
610
|
-
|
586
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
611
587
|
if reverse
|
612
588
|
key = [row[:schema], row[:table], row[:name]]
|
613
589
|
else
|
@@ -642,45 +618,44 @@ module Sequel
|
|
642
618
|
def freeze
|
643
619
|
server_version
|
644
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
|
645
629
|
@conversion_procs.freeze
|
646
630
|
super
|
647
631
|
end
|
648
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
|
+
|
649
651
|
# Use the pg_* system tables to determine indexes on a table
|
650
652
|
def indexes(table, opts=OPTS)
|
651
653
|
m = output_identifier_meth
|
652
|
-
|
653
|
-
|
654
|
-
if server_version >= 90500
|
655
|
-
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
656
|
-
else
|
657
|
-
range = 0...32
|
658
|
-
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
659
|
-
end
|
660
|
-
|
661
|
-
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
662
|
-
|
663
|
-
ds = metadata_dataset.
|
664
|
-
from{pg_class.as(:tab)}.
|
665
|
-
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
666
|
-
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
667
|
-
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
668
|
-
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
669
|
-
where{{
|
670
|
-
indc[:relkind]=>'i',
|
671
|
-
ind[:indisprimary]=>false,
|
672
|
-
:indexprs=>nil,
|
673
|
-
:indisvalid=>true,
|
674
|
-
tab[:oid]=>oid}}.
|
675
|
-
order(*order).
|
676
|
-
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
677
|
-
|
678
|
-
ds = ds.where(:indpred=>nil) unless opts[:include_partial]
|
679
|
-
ds = ds.where(:indisready=>true) if server_version >= 80300
|
680
|
-
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]
|
681
656
|
|
682
657
|
indexes = {}
|
683
|
-
|
658
|
+
_indexes_ds.where_each(cond) do |r|
|
684
659
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
685
660
|
i[:columns] << m.call(r[:column])
|
686
661
|
end
|
@@ -713,8 +688,7 @@ module Sequel
|
|
713
688
|
def primary_key(table, opts=OPTS)
|
714
689
|
quoted_table = quote_schema_table(table)
|
715
690
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
716
|
-
|
717
|
-
value = fetch(sql).single_value
|
691
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
718
692
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
719
693
|
end
|
720
694
|
|
@@ -722,24 +696,21 @@ module Sequel
|
|
722
696
|
def primary_key_sequence(table, opts=OPTS)
|
723
697
|
quoted_table = quote_schema_table(table)
|
724
698
|
Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
|
725
|
-
|
726
|
-
if pks =
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
|
731
|
-
if pks = fetch(sql).single_record
|
732
|
-
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
733
|
-
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
734
|
-
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])))
|
735
704
|
end
|
705
|
+
|
706
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
736
707
|
end
|
737
708
|
|
738
709
|
# Refresh the materialized view with the given name.
|
739
710
|
#
|
740
711
|
# DB.refresh_view(:items_view)
|
741
712
|
# # REFRESH MATERIALIZED VIEW items_view
|
742
|
-
# DB.refresh_view(:items_view, :
|
713
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
743
714
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
744
715
|
def refresh_view(name, opts=OPTS)
|
745
716
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
@@ -758,10 +729,12 @@ module Sequel
|
|
758
729
|
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
759
730
|
increment_by = :seqincrement
|
760
731
|
min_value = :seqmin
|
732
|
+
# :nocov:
|
761
733
|
else
|
762
734
|
seq_ds = metadata_dataset.from(LiteralString.new(seq))
|
763
735
|
increment_by = :increment_by
|
764
736
|
min_value = :min_value
|
737
|
+
# :nocov:
|
765
738
|
end
|
766
739
|
|
767
740
|
get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
|
@@ -774,7 +747,9 @@ module Sequel
|
|
774
747
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
775
748
|
# managing incrementing primary keys.
|
776
749
|
def serial_primary_key_options
|
750
|
+
# :nocov:
|
777
751
|
auto_increment_key = server_version >= 100002 ? :identity : :serial
|
752
|
+
# :nocov:
|
778
753
|
{:primary_key => true, auto_increment_key => true, :type=>Integer}
|
779
754
|
end
|
780
755
|
|
@@ -867,6 +842,7 @@ module Sequel
|
|
867
842
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
868
843
|
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
869
844
|
def values(v)
|
845
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
870
846
|
@default_dataset.clone(:values=>v)
|
871
847
|
end
|
872
848
|
|
@@ -883,8 +859,282 @@ module Sequel
|
|
883
859
|
pg_class_relname(relkind, opts)
|
884
860
|
end
|
885
861
|
|
862
|
+
# Attempt to acquire an exclusive advisory lock with the given lock_id (which should be
|
863
|
+
# a 64-bit integer). If successful, yield to the block, then release the advisory lock
|
864
|
+
# when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
|
865
|
+
#
|
866
|
+
# DB.with_advisory_lock(1347){DB.get(1)}
|
867
|
+
# # SELECT pg_try_advisory_lock(1357) LIMIT 1
|
868
|
+
# # SELECT 1 AS v LIMIT 1
|
869
|
+
# # SELECT pg_advisory_unlock(1357) LIMIT 1
|
870
|
+
#
|
871
|
+
# Options:
|
872
|
+
# :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
|
873
|
+
def with_advisory_lock(lock_id, opts=OPTS)
|
874
|
+
ds = dataset
|
875
|
+
if server = opts[:server]
|
876
|
+
ds = ds.server(server)
|
877
|
+
end
|
878
|
+
|
879
|
+
synchronize(server) do |c|
|
880
|
+
begin
|
881
|
+
if opts[:wait]
|
882
|
+
ds.get{pg_advisory_lock(lock_id)}
|
883
|
+
locked = true
|
884
|
+
else
|
885
|
+
unless locked = ds.get{pg_try_advisory_lock(lock_id)}
|
886
|
+
raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
yield
|
891
|
+
ensure
|
892
|
+
ds.get{pg_advisory_unlock(lock_id)} if locked
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
886
897
|
private
|
887
898
|
|
899
|
+
# Dataset used to retrieve CHECK constraint information
|
900
|
+
def _check_constraints_ds
|
901
|
+
@_check_constraints_ds ||= metadata_dataset.
|
902
|
+
from{pg_constraint.as(:co)}.
|
903
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
904
|
+
where(:contype=>'c').
|
905
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
906
|
+
end
|
907
|
+
|
908
|
+
# Dataset used to retrieve foreign keys referenced by a table
|
909
|
+
def _foreign_key_list_ds
|
910
|
+
@_foreign_key_list_ds ||= __foreign_key_list_ds(false)
|
911
|
+
end
|
912
|
+
|
913
|
+
# Dataset used to retrieve foreign keys referencing a table
|
914
|
+
def _reverse_foreign_key_list_ds
|
915
|
+
@_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
|
916
|
+
end
|
917
|
+
|
918
|
+
# Build dataset used for foreign key list methods.
|
919
|
+
def __foreign_key_list_ds(reverse)
|
920
|
+
if reverse
|
921
|
+
ctable = Sequel[:att2]
|
922
|
+
cclass = Sequel[:cl2]
|
923
|
+
rtable = Sequel[:att]
|
924
|
+
rclass = Sequel[:cl]
|
925
|
+
else
|
926
|
+
ctable = Sequel[:att]
|
927
|
+
cclass = Sequel[:cl]
|
928
|
+
rtable = Sequel[:att2]
|
929
|
+
rclass = Sequel[:cl2]
|
930
|
+
end
|
931
|
+
|
932
|
+
if server_version >= 90500
|
933
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
934
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
935
|
+
# :nocov:
|
936
|
+
else
|
937
|
+
range = 0...32
|
938
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
939
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
940
|
+
# :nocov:
|
941
|
+
end
|
942
|
+
|
943
|
+
ds = metadata_dataset.
|
944
|
+
from{pg_constraint.as(:co)}.
|
945
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
946
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
947
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
948
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
949
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
950
|
+
order{[co[:conname], cpos]}.
|
951
|
+
where{{
|
952
|
+
cl[:relkind]=>%w'r p',
|
953
|
+
co[:contype]=>'f',
|
954
|
+
cpos=>rpos
|
955
|
+
}}.
|
956
|
+
select{[
|
957
|
+
co[:conname].as(:name),
|
958
|
+
ctable[:attname].as(:column),
|
959
|
+
co[:confupdtype].as(:on_update),
|
960
|
+
co[:confdeltype].as(:on_delete),
|
961
|
+
cl2[:relname].as(:table),
|
962
|
+
rtable[:attname].as(:refcolumn),
|
963
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
964
|
+
nsp[:nspname].as(:schema)
|
965
|
+
]}
|
966
|
+
|
967
|
+
if reverse
|
968
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
969
|
+
end
|
970
|
+
|
971
|
+
ds
|
972
|
+
end
|
973
|
+
|
974
|
+
# Dataset used to retrieve index information
|
975
|
+
def _indexes_ds
|
976
|
+
@_indexes_ds ||= begin
|
977
|
+
if server_version >= 90500
|
978
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
979
|
+
# :nocov:
|
980
|
+
else
|
981
|
+
range = 0...32
|
982
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
983
|
+
# :nocov:
|
984
|
+
end
|
985
|
+
|
986
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
987
|
+
|
988
|
+
ds = metadata_dataset.
|
989
|
+
from{pg_class.as(:tab)}.
|
990
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
991
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
992
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
993
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
994
|
+
where{{
|
995
|
+
indc[:relkind]=>%w'i I',
|
996
|
+
ind[:indisprimary]=>false,
|
997
|
+
:indexprs=>nil,
|
998
|
+
:indisvalid=>true}}.
|
999
|
+
order(*order).
|
1000
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
1001
|
+
|
1002
|
+
# :nocov:
|
1003
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
1004
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
1005
|
+
# :nocov:
|
1006
|
+
|
1007
|
+
ds
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# Dataset used to determine custom serial sequences for tables
|
1012
|
+
def _select_custom_sequence_ds
|
1013
|
+
@_select_custom_sequence_ds ||= metadata_dataset.
|
1014
|
+
from{pg_class.as(:t)}.
|
1015
|
+
join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
|
1016
|
+
join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
|
1017
|
+
join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
|
1018
|
+
join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
|
1019
|
+
where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
|
1020
|
+
select{
|
1021
|
+
expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
|
1022
|
+
[
|
1023
|
+
name[:nspname].as(:schema),
|
1024
|
+
Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
|
1025
|
+
]
|
1026
|
+
}
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
# Dataset used to determine normal serial sequences for tables
|
1030
|
+
def _select_serial_sequence_ds
|
1031
|
+
@_serial_sequence_ds ||= metadata_dataset.
|
1032
|
+
from{[
|
1033
|
+
pg_class.as(:seq),
|
1034
|
+
pg_attribute.as(:attr),
|
1035
|
+
pg_depend.as(:dep),
|
1036
|
+
pg_namespace.as(:name),
|
1037
|
+
pg_constraint.as(:cons),
|
1038
|
+
pg_class.as(:t)
|
1039
|
+
]}.
|
1040
|
+
where{[
|
1041
|
+
[seq[:oid], dep[:objid]],
|
1042
|
+
[seq[:relnamespace], name[:oid]],
|
1043
|
+
[seq[:relkind], 'S'],
|
1044
|
+
[attr[:attrelid], dep[:refobjid]],
|
1045
|
+
[attr[:attnum], dep[:refobjsubid]],
|
1046
|
+
[attr[:attrelid], cons[:conrelid]],
|
1047
|
+
[attr[:attnum], cons[:conkey].sql_subscript(1)],
|
1048
|
+
[attr[:attrelid], t[:oid]],
|
1049
|
+
[cons[:contype], 'p']
|
1050
|
+
]}.
|
1051
|
+
select{[
|
1052
|
+
name[:nspname].as(:schema),
|
1053
|
+
seq[:relname].as(:sequence)
|
1054
|
+
]}
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
# Dataset used to determine primary keys for tables
|
1058
|
+
def _select_pk_ds
|
1059
|
+
@_select_pk_ds ||= metadata_dataset.
|
1060
|
+
from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
|
1061
|
+
where{[
|
1062
|
+
[pg_class[:oid], pg_attribute[:attrelid]],
|
1063
|
+
[pg_class[:relnamespace], pg_namespace[:oid]],
|
1064
|
+
[pg_class[:oid], pg_index[:indrelid]],
|
1065
|
+
[pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
|
1066
|
+
[pg_index[:indisprimary], 't']
|
1067
|
+
]}.
|
1068
|
+
select{pg_attribute[:attname].as(:pk)}
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
# Dataset used to get schema for tables
|
1072
|
+
def _schema_ds
|
1073
|
+
@_schema_ds ||= begin
|
1074
|
+
ds = metadata_dataset.select{[
|
1075
|
+
pg_attribute[:attname].as(:name),
|
1076
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1077
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1078
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1079
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1080
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1081
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1082
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key),
|
1083
|
+
Sequel[:pg_type][:typtype],
|
1084
|
+
(~Sequel[Sequel[:elementtype][:oid]=>nil]).as(:is_array),
|
1085
|
+
]}.
|
1086
|
+
from(:pg_class).
|
1087
|
+
join(:pg_attribute, :attrelid=>:oid).
|
1088
|
+
join(:pg_type, :oid=>:atttypid).
|
1089
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1090
|
+
left_outer_join(Sequel[:pg_type].as(:elementtype), :typarray=>Sequel[:pg_type][:oid]).
|
1091
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1092
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1093
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
1094
|
+
where{pg_attribute[:attnum] > 0}.
|
1095
|
+
order{pg_attribute[:attnum]}
|
1096
|
+
|
1097
|
+
# :nocov:
|
1098
|
+
if server_version > 100000
|
1099
|
+
# :nocov:
|
1100
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
1101
|
+
|
1102
|
+
# :nocov:
|
1103
|
+
if server_version > 120000
|
1104
|
+
# :nocov:
|
1105
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
ds
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
# Internals of defer_constraints/immediate_constraints
|
1114
|
+
def _set_constraints(type, opts)
|
1115
|
+
execute_ddl(_set_constraints_sql(type, opts), opts)
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
# SQL to use for SET CONSTRAINTS
|
1119
|
+
def _set_constraints_sql(type, opts)
|
1120
|
+
sql = String.new
|
1121
|
+
sql << "SET CONSTRAINTS "
|
1122
|
+
if constraints = opts[:constraints]
|
1123
|
+
dataset.send(:source_list_append, sql, Array(constraints))
|
1124
|
+
else
|
1125
|
+
sql << "ALL"
|
1126
|
+
end
|
1127
|
+
sql << type
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
# Consider lock or statement timeout errors as evidence that the table exists
|
1131
|
+
# but is locked.
|
1132
|
+
def _table_exists?(ds)
|
1133
|
+
super
|
1134
|
+
rescue DatabaseError => e
|
1135
|
+
raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
|
1136
|
+
end
|
1137
|
+
|
888
1138
|
def alter_table_add_column_sql(table, op)
|
889
1139
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
890
1140
|
end
|
@@ -1152,7 +1402,7 @@ module Sequel
|
|
1152
1402
|
when :hash
|
1153
1403
|
mod, remainder = generator.hash_values
|
1154
1404
|
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
1155
|
-
when :default
|
1405
|
+
else # when :default
|
1156
1406
|
sql << " DEFAULT"
|
1157
1407
|
end
|
1158
1408
|
|
@@ -1247,6 +1497,10 @@ module Sequel
|
|
1247
1497
|
def create_view_prefix_sql(name, options)
|
1248
1498
|
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])
|
1249
1499
|
|
1500
|
+
if options[:security_invoker]
|
1501
|
+
sql += " WITH (security_invoker)"
|
1502
|
+
end
|
1503
|
+
|
1250
1504
|
if tablespace = options[:tablespace]
|
1251
1505
|
sql += " TABLESPACE #{quote_identifier(tablespace)}"
|
1252
1506
|
end
|
@@ -1294,7 +1548,11 @@ module Sequel
|
|
1294
1548
|
# currently visible schemas.
|
1295
1549
|
def filter_schema(ds, opts)
|
1296
1550
|
expr = if schema = opts[:schema]
|
1297
|
-
schema.
|
1551
|
+
if schema.is_a?(SQL::Identifier)
|
1552
|
+
schema.value.to_s
|
1553
|
+
else
|
1554
|
+
schema.to_s
|
1555
|
+
end
|
1298
1556
|
else
|
1299
1557
|
Sequel.function(:any, Sequel.function(:current_schemas, false))
|
1300
1558
|
end
|
@@ -1304,16 +1562,20 @@ module Sequel
|
|
1304
1562
|
def index_definition_sql(table_name, index)
|
1305
1563
|
cols = index[:columns]
|
1306
1564
|
index_name = index[:name] || default_index_name(table_name, cols)
|
1565
|
+
|
1307
1566
|
expr = if o = index[:opclass]
|
1308
1567
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
1309
1568
|
else
|
1310
1569
|
literal(Array(cols))
|
1311
1570
|
end
|
1571
|
+
|
1312
1572
|
if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
|
1313
1573
|
unique = "UNIQUE " if index[:unique]
|
1314
1574
|
index_type = index[:type]
|
1315
1575
|
filter = index[:where] || index[:filter]
|
1316
1576
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
1577
|
+
nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
|
1578
|
+
|
1317
1579
|
case index_type
|
1318
1580
|
when :full_text
|
1319
1581
|
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
@@ -1321,7 +1583,8 @@ module Sequel
|
|
1321
1583
|
when :spatial
|
1322
1584
|
index_type = :gist
|
1323
1585
|
end
|
1324
|
-
|
1586
|
+
|
1587
|
+
"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}"
|
1325
1588
|
end
|
1326
1589
|
|
1327
1590
|
# Setup datastructures shared by all postgres adapters.
|
@@ -1347,11 +1610,6 @@ module Sequel
|
|
1347
1610
|
end
|
1348
1611
|
end
|
1349
1612
|
|
1350
|
-
# Use a dollar sign instead of question mark for the argument placeholder.
|
1351
|
-
def prepared_arg_placeholder
|
1352
|
-
PREPARED_ARG_PLACEHOLDER
|
1353
|
-
end
|
1354
|
-
|
1355
1613
|
# Return an expression the oid for the table expr. Used by the metadata parsing
|
1356
1614
|
# code to disambiguate unqualified tables.
|
1357
1615
|
def regclass_oid(expr, opts=OPTS)
|
@@ -1385,11 +1643,12 @@ module Sequel
|
|
1385
1643
|
end
|
1386
1644
|
|
1387
1645
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
1388
|
-
# a rename table operation, so
|
1646
|
+
# a rename table operation, so specifying a new schema in new_name will not have an effect.
|
1389
1647
|
def rename_table_sql(name, new_name)
|
1390
1648
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
1391
1649
|
end
|
1392
1650
|
|
1651
|
+
# Handle interval and citext types.
|
1393
1652
|
def schema_column_type(db_type)
|
1394
1653
|
case db_type
|
1395
1654
|
when /\Ainterval\z/io
|
@@ -1401,39 +1660,48 @@ module Sequel
|
|
1401
1660
|
end
|
1402
1661
|
end
|
1403
1662
|
|
1663
|
+
# The schema :type entry to use for array types.
|
1664
|
+
def schema_array_type(db_type)
|
1665
|
+
:array
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
# The schema :type entry to use for row/composite types.
|
1669
|
+
def schema_composite_type(db_type)
|
1670
|
+
:composite
|
1671
|
+
end
|
1672
|
+
|
1673
|
+
# The schema :type entry to use for enum types.
|
1674
|
+
def schema_enum_type(db_type)
|
1675
|
+
:enum
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
# The schema :type entry to use for range types.
|
1679
|
+
def schema_range_type(db_type)
|
1680
|
+
:range
|
1681
|
+
end
|
1682
|
+
|
1683
|
+
# The schema :type entry to use for multirange types.
|
1684
|
+
def schema_multirange_type(db_type)
|
1685
|
+
:multirange
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
MIN_DATE = Date.new(-4713, 11, 24)
|
1689
|
+
MAX_DATE = Date.new(5874897, 12, 31)
|
1690
|
+
MIN_TIMESTAMP = Time.utc(-4713, 11, 24).freeze
|
1691
|
+
MAX_TIMESTAMP = (Time.utc(294277) - Rational(1, 1000000)).freeze
|
1692
|
+
TYPTYPE_METHOD_MAP = {
|
1693
|
+
'c' => :schema_composite_type,
|
1694
|
+
'e' => :schema_enum_type,
|
1695
|
+
'r' => :schema_range_type,
|
1696
|
+
'm' => :schema_multirange_type,
|
1697
|
+
}
|
1698
|
+
TYPTYPE_METHOD_MAP.default = :schema_column_type
|
1699
|
+
TYPTYPE_METHOD_MAP.freeze
|
1404
1700
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
1405
1701
|
def schema_parse_table(table_name, opts)
|
1406
1702
|
m = output_identifier_meth(opts[:dataset])
|
1407
|
-
oid = regclass_oid(table_name, opts)
|
1408
|
-
ds = metadata_dataset.select{[
|
1409
|
-
pg_attribute[:attname].as(:name),
|
1410
|
-
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1411
|
-
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1412
|
-
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1413
|
-
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1414
|
-
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1415
|
-
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1416
|
-
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
1417
|
-
from(:pg_class).
|
1418
|
-
join(:pg_attribute, :attrelid=>:oid).
|
1419
|
-
join(:pg_type, :oid=>:atttypid).
|
1420
|
-
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1421
|
-
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1422
|
-
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1423
|
-
where{{pg_attribute[:attisdropped]=>false}}.
|
1424
|
-
where{pg_attribute[:attnum] > 0}.
|
1425
|
-
where{{pg_class[:oid]=>oid}}.
|
1426
|
-
order{pg_attribute[:attnum]}
|
1427
|
-
|
1428
|
-
if server_version > 100000
|
1429
|
-
ds = ds.select_append{pg_attribute[:attidentity]}
|
1430
|
-
|
1431
|
-
if server_version > 120000
|
1432
|
-
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1433
|
-
end
|
1434
|
-
end
|
1435
1703
|
|
1436
|
-
|
1704
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
1437
1705
|
row[:default] = nil if blank_object?(row[:default])
|
1438
1706
|
if row[:base_oid]
|
1439
1707
|
row[:domain_oid] = row[:oid]
|
@@ -1444,11 +1712,33 @@ module Sequel
|
|
1444
1712
|
row.delete(:base_oid)
|
1445
1713
|
row.delete(:db_base_type)
|
1446
1714
|
end
|
1447
|
-
|
1715
|
+
|
1716
|
+
db_type = row[:db_type]
|
1717
|
+
row[:type] = if row.delete(:is_array)
|
1718
|
+
schema_array_type(db_type)
|
1719
|
+
else
|
1720
|
+
send(TYPTYPE_METHOD_MAP[row.delete(:typtype)], db_type)
|
1721
|
+
end
|
1448
1722
|
identity = row.delete(:attidentity)
|
1449
1723
|
if row[:primary_key]
|
1450
1724
|
row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
|
1451
1725
|
end
|
1726
|
+
|
1727
|
+
# :nocov:
|
1728
|
+
if server_version >= 90600
|
1729
|
+
# :nocov:
|
1730
|
+
case row[:oid]
|
1731
|
+
when 1082
|
1732
|
+
row[:min_value] = MIN_DATE
|
1733
|
+
row[:max_value] = MAX_DATE
|
1734
|
+
when 1184, 1114
|
1735
|
+
if Sequel.datetime_class == Time
|
1736
|
+
row[:min_value] = MIN_TIMESTAMP
|
1737
|
+
row[:max_value] = MAX_TIMESTAMP
|
1738
|
+
end
|
1739
|
+
end
|
1740
|
+
end
|
1741
|
+
|
1452
1742
|
[m.call(row.delete(:name)), row]
|
1453
1743
|
end
|
1454
1744
|
end
|
@@ -1516,7 +1806,9 @@ module Sequel
|
|
1516
1806
|
|
1517
1807
|
# PostgreSQL 9.4+ supports views with check option.
|
1518
1808
|
def view_with_check_option_support
|
1809
|
+
# :nocov:
|
1519
1810
|
:local if server_version >= 90400
|
1811
|
+
# :nocov:
|
1520
1812
|
end
|
1521
1813
|
end
|
1522
1814
|
|
@@ -1528,7 +1820,7 @@ module Sequel
|
|
1528
1820
|
|
1529
1821
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1530
1822
|
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']])
|
1531
|
-
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1823
|
+
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values compounds 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']])
|
1532
1824
|
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']])
|
1533
1825
|
|
1534
1826
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
@@ -1554,8 +1846,6 @@ module Sequel
|
|
1554
1846
|
literal_append(sql, args[0])
|
1555
1847
|
sql << ' ' << op.to_s << ' '
|
1556
1848
|
literal_append(sql, args[1])
|
1557
|
-
sql << " ESCAPE "
|
1558
|
-
literal_append(sql, "\\")
|
1559
1849
|
sql << ')'
|
1560
1850
|
else
|
1561
1851
|
super
|
@@ -1580,6 +1870,12 @@ module Sequel
|
|
1580
1870
|
clone(:disable_insert_returning=>true)
|
1581
1871
|
end
|
1582
1872
|
|
1873
|
+
# Always return false when using VALUES
|
1874
|
+
def empty?
|
1875
|
+
return false if @opts[:values]
|
1876
|
+
super
|
1877
|
+
end
|
1878
|
+
|
1583
1879
|
# Return the results of an EXPLAIN query as a string
|
1584
1880
|
def explain(opts=OPTS)
|
1585
1881
|
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
@@ -1603,7 +1899,7 @@ module Sequel
|
|
1603
1899
|
# :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
|
1604
1900
|
# returned rows also include the exact phrase used.
|
1605
1901
|
# :rank :: Set to true to order by the rank, so that closer matches are returned first.
|
1606
|
-
# :to_tsquery :: Can be set to :plain or :
|
1902
|
+
# :to_tsquery :: Can be set to :plain, :phrase, or :websearch to specify the function to use to
|
1607
1903
|
# convert the terms to a ts_query.
|
1608
1904
|
# :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
|
1609
1905
|
# tsquery, and can be used directly in the query.
|
@@ -1623,6 +1919,8 @@ module Sequel
|
|
1623
1919
|
query_func = case to_tsquery = opts[:to_tsquery]
|
1624
1920
|
when :phrase, :plain
|
1625
1921
|
:"#{to_tsquery}to_tsquery"
|
1922
|
+
when :websearch
|
1923
|
+
:"websearch_to_tsquery"
|
1626
1924
|
else
|
1627
1925
|
(opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
1628
1926
|
end
|
@@ -1887,10 +2185,14 @@ module Sequel
|
|
1887
2185
|
server_version >= 90500
|
1888
2186
|
end
|
1889
2187
|
|
2188
|
+
# :nocov:
|
2189
|
+
|
1890
2190
|
# PostgreSQL supports timezones in literal timestamps
|
1891
2191
|
def supports_timestamp_timezones?
|
2192
|
+
# SEQUEL6: Remove
|
1892
2193
|
true
|
1893
2194
|
end
|
2195
|
+
# :nocov:
|
1894
2196
|
|
1895
2197
|
# PostgreSQL 8.4+ supports WINDOW clause.
|
1896
2198
|
def supports_window_clause?
|
@@ -1912,6 +2214,8 @@ module Sequel
|
|
1912
2214
|
server_version >= 90000
|
1913
2215
|
when :groups, :exclude
|
1914
2216
|
server_version >= 110000
|
2217
|
+
else
|
2218
|
+
false
|
1915
2219
|
end
|
1916
2220
|
end
|
1917
2221
|
|
@@ -1954,12 +2258,10 @@ module Sequel
|
|
1954
2258
|
# Otherwise, return an array of hashes.
|
1955
2259
|
def _import(columns, values, opts=OPTS)
|
1956
2260
|
if @opts[:returning]
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
statements.map{|st| returning_fetch_rows(st)}
|
1962
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
2261
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
2262
|
+
# that there's only ever a single statement.
|
2263
|
+
sql = multi_insert_sql(columns, values)[0]
|
2264
|
+
returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
|
1963
2265
|
elsif opts[:return] == :primary_key
|
1964
2266
|
returning(insert_pk)._import(columns, values, opts)
|
1965
2267
|
else
|
@@ -1999,12 +2301,22 @@ module Sequel
|
|
1999
2301
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
2000
2302
|
end
|
2001
2303
|
|
2304
|
+
# Use from_self for aggregate dataset using VALUES.
|
2305
|
+
def aggreate_dataset_use_from_self?
|
2306
|
+
super || @opts[:values]
|
2307
|
+
end
|
2308
|
+
|
2002
2309
|
# Allow truncation of multiple source tables.
|
2003
2310
|
def check_truncation_allowed!
|
2004
2311
|
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
2005
2312
|
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
2006
2313
|
end
|
2007
2314
|
|
2315
|
+
# The strftime format to use when literalizing the time.
|
2316
|
+
def default_timestamp_format
|
2317
|
+
"'%Y-%m-%d %H:%M:%S.%6N%z'"
|
2318
|
+
end
|
2319
|
+
|
2008
2320
|
# Only include the primary table in the main delete clause
|
2009
2321
|
def delete_from_sql(sql)
|
2010
2322
|
sql << ' FROM '
|
@@ -2058,12 +2370,11 @@ module Sequel
|
|
2058
2370
|
|
2059
2371
|
# Return the primary key to use for RETURNING in an INSERT statement
|
2060
2372
|
def insert_pk
|
2061
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
end
|
2373
|
+
(f = opts[:from]) && !f.empty? && (t = f.first)
|
2374
|
+
case t
|
2375
|
+
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
2376
|
+
if pk = db.primary_key(t)
|
2377
|
+
Sequel::SQL::Identifier.new(pk)
|
2067
2378
|
end
|
2068
2379
|
end
|
2069
2380
|
end
|
@@ -2124,6 +2435,22 @@ module Sequel
|
|
2124
2435
|
end
|
2125
2436
|
end
|
2126
2437
|
|
2438
|
+
# Handle Ruby integers outside PostgreSQL bigint range specially.
|
2439
|
+
def literal_integer(v)
|
2440
|
+
if v > 9223372036854775807 || v < -9223372036854775808
|
2441
|
+
literal_integer_outside_bigint_range(v)
|
2442
|
+
else
|
2443
|
+
v.to_s
|
2444
|
+
end
|
2445
|
+
end
|
2446
|
+
|
2447
|
+
# Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
|
2448
|
+
# outside PostgreSQL bigint range, so PostgreSQL doesn't treat
|
2449
|
+
# the value as numeric.
|
2450
|
+
def literal_integer_outside_bigint_range(v)
|
2451
|
+
raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
|
2452
|
+
end
|
2453
|
+
|
2127
2454
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
2128
2455
|
def literal_string_append(sql, v)
|
2129
2456
|
sql << "'" << v.gsub("'", "''") << "'"
|
@@ -2222,7 +2549,10 @@ module Sequel
|
|
2222
2549
|
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
2223
2550
|
def select_with_sql_cte(sql, cte)
|
2224
2551
|
super
|
2552
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
2553
|
+
end
|
2225
2554
|
|
2555
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
2226
2556
|
if search_opts = cte[:search]
|
2227
2557
|
sql << if search_opts[:type] == :breadth
|
2228
2558
|
" SEARCH BREADTH FIRST BY "
|