sequel 5.60.1 → 5.81.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 +280 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +27 -24
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/code_order.rdoc +5 -3
- data/doc/dataset_basics.rdoc +1 -1
- 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 +18 -7
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +7 -2
- 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/release_notes/5.79.0.txt +28 -0
- data/doc/release_notes/5.80.0.txt +40 -0
- data/doc/release_notes/5.81.0.txt +31 -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 +10 -5
- 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 +82 -3
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +469 -160
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +39 -3
- 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 +1 -1
- data/lib/sequel/database/schema_methods.rb +12 -5
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/dataset_module.rb +1 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/graph.rb +1 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +119 -45
- 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/caller_logging.rb +4 -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 +1 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -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 +3 -3
- 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/provenance.rb +110 -0
- 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/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +38 -11
- data/lib/sequel/model/base.rb +32 -18
- 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 +28 -7
- 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/input_transformer.rb +1 -1
- data/lib/sequel/plugins/list.rb +5 -2
- 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/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- 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 +14 -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 +2 -2
- metadata +79 -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
|
|
@@ -253,7 +267,7 @@ module Sequel
|
|
|
253
267
|
WHERE cons.contype = 'p'
|
|
254
268
|
AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
|
|
255
269
|
end_sql
|
|
256
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
270
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
257
271
|
|
|
258
272
|
# SQL fragment for determining primary key column for the given table. Only
|
|
259
273
|
# returns the first primary key if the table has a composite primary key.
|
|
@@ -266,7 +280,7 @@ module Sequel
|
|
|
266
280
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
|
267
281
|
AND pg_index.indisprimary = 't'
|
|
268
282
|
end_sql
|
|
269
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
283
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
270
284
|
|
|
271
285
|
# SQL fragment for getting sequence associated with table's
|
|
272
286
|
# primary key, assuming it was a serial primary key column.
|
|
@@ -284,7 +298,7 @@ module Sequel
|
|
|
284
298
|
AND attr.attrelid = t.oid
|
|
285
299
|
AND cons.contype = 'p'
|
|
286
300
|
end_sql
|
|
287
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
301
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
288
302
|
|
|
289
303
|
# A hash of conversion procs, keyed by type integer (oid) and
|
|
290
304
|
# having callable values for the conversion proc for that type.
|
|
@@ -318,14 +332,8 @@ module Sequel
|
|
|
318
332
|
def check_constraints(table)
|
|
319
333
|
m = output_identifier_meth
|
|
320
334
|
|
|
321
|
-
rows = metadata_dataset.
|
|
322
|
-
from{pg_constraint.as(:co)}.
|
|
323
|
-
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
324
|
-
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
|
325
|
-
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
326
|
-
|
|
327
335
|
hash = {}
|
|
328
|
-
|
|
336
|
+
_check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
|
|
329
337
|
constraint = m.call(row[:constraint])
|
|
330
338
|
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
|
331
339
|
entry[:columns] << m.call(row[:column]) if row[: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,65 +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
|
-
# :nocov:
|
|
573
|
-
else
|
|
574
|
-
range = 0...32
|
|
575
|
-
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
|
576
|
-
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
|
577
|
-
# :nocov:
|
|
578
|
-
end
|
|
579
|
-
|
|
580
|
-
ds = metadata_dataset.
|
|
581
|
-
from{pg_constraint.as(:co)}.
|
|
582
|
-
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
|
583
|
-
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
584
|
-
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
|
585
|
-
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
|
586
|
-
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
|
587
|
-
order{[co[:conname], cpos]}.
|
|
588
|
-
where{{
|
|
589
|
-
cl[:relkind]=>'r',
|
|
590
|
-
co[:contype]=>'f',
|
|
591
|
-
cl[:oid]=>oid,
|
|
592
|
-
cpos=>rpos
|
|
593
|
-
}}.
|
|
594
|
-
select{[
|
|
595
|
-
co[:conname].as(:name),
|
|
596
|
-
ctable[:attname].as(:column),
|
|
597
|
-
co[:confupdtype].as(:on_update),
|
|
598
|
-
co[:confdeltype].as(:on_delete),
|
|
599
|
-
cl2[:relname].as(:table),
|
|
600
|
-
rtable[:attname].as(:refcolumn),
|
|
601
|
-
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
|
602
|
-
nsp[:nspname].as(:schema)
|
|
603
|
-
]}
|
|
604
|
-
|
|
605
|
-
if reverse
|
|
606
|
-
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
|
607
|
-
end
|
|
608
581
|
|
|
609
582
|
h = {}
|
|
610
583
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
584
|
+
reverse = opts[:reverse]
|
|
611
585
|
|
|
612
|
-
|
|
586
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
|
613
587
|
if reverse
|
|
614
588
|
key = [row[:schema], row[:table], row[:name]]
|
|
615
589
|
else
|
|
@@ -644,49 +618,44 @@ module Sequel
|
|
|
644
618
|
def freeze
|
|
645
619
|
server_version
|
|
646
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
|
|
647
629
|
@conversion_procs.freeze
|
|
648
630
|
super
|
|
649
631
|
end
|
|
650
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
|
+
|
|
651
651
|
# Use the pg_* system tables to determine indexes on a table
|
|
652
652
|
def indexes(table, opts=OPTS)
|
|
653
653
|
m = output_identifier_meth
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if server_version >= 90500
|
|
657
|
-
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
|
658
|
-
# :nocov:
|
|
659
|
-
else
|
|
660
|
-
range = 0...32
|
|
661
|
-
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
|
662
|
-
# :nocov:
|
|
663
|
-
end
|
|
664
|
-
|
|
665
|
-
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
|
666
|
-
|
|
667
|
-
ds = metadata_dataset.
|
|
668
|
-
from{pg_class.as(:tab)}.
|
|
669
|
-
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
|
670
|
-
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
|
671
|
-
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
|
672
|
-
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
|
673
|
-
where{{
|
|
674
|
-
indc[:relkind]=>'i',
|
|
675
|
-
ind[:indisprimary]=>false,
|
|
676
|
-
:indexprs=>nil,
|
|
677
|
-
:indisvalid=>true,
|
|
678
|
-
tab[:oid]=>oid}}.
|
|
679
|
-
order(*order).
|
|
680
|
-
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
|
681
|
-
|
|
682
|
-
ds = ds.where(:indpred=>nil) unless opts[:include_partial]
|
|
683
|
-
# :nocov:
|
|
684
|
-
ds = ds.where(:indisready=>true) if server_version >= 80300
|
|
685
|
-
ds = ds.where(:indislive=>true) if server_version >= 90300
|
|
686
|
-
# :nocov:
|
|
654
|
+
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
|
655
|
+
cond[:indpred] = nil unless opts[:include_partial]
|
|
687
656
|
|
|
688
657
|
indexes = {}
|
|
689
|
-
|
|
658
|
+
_indexes_ds.where_each(cond) do |r|
|
|
690
659
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
|
691
660
|
i[:columns] << m.call(r[:column])
|
|
692
661
|
end
|
|
@@ -719,8 +688,7 @@ module Sequel
|
|
|
719
688
|
def primary_key(table, opts=OPTS)
|
|
720
689
|
quoted_table = quote_schema_table(table)
|
|
721
690
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
|
722
|
-
|
|
723
|
-
value = fetch(sql).single_value
|
|
691
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
|
724
692
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
|
725
693
|
end
|
|
726
694
|
|
|
@@ -728,24 +696,21 @@ module Sequel
|
|
|
728
696
|
def primary_key_sequence(table, opts=OPTS)
|
|
729
697
|
quoted_table = quote_schema_table(table)
|
|
730
698
|
Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
|
|
731
|
-
|
|
732
|
-
if pks =
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
|
|
737
|
-
if pks = fetch(sql).single_record
|
|
738
|
-
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
739
|
-
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
|
740
|
-
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])))
|
|
741
704
|
end
|
|
705
|
+
|
|
706
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
|
742
707
|
end
|
|
743
708
|
|
|
744
709
|
# Refresh the materialized view with the given name.
|
|
745
710
|
#
|
|
746
711
|
# DB.refresh_view(:items_view)
|
|
747
712
|
# # REFRESH MATERIALIZED VIEW items_view
|
|
748
|
-
# DB.refresh_view(:items_view, :
|
|
713
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
|
749
714
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
|
750
715
|
def refresh_view(name, opts=OPTS)
|
|
751
716
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
|
@@ -877,6 +842,7 @@ module Sequel
|
|
|
877
842
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
|
878
843
|
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
|
879
844
|
def values(v)
|
|
845
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
|
880
846
|
@default_dataset.clone(:values=>v)
|
|
881
847
|
end
|
|
882
848
|
|
|
@@ -893,8 +859,282 @@ module Sequel
|
|
|
893
859
|
pg_class_relname(relkind, opts)
|
|
894
860
|
end
|
|
895
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
|
+
|
|
896
897
|
private
|
|
897
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
|
+
|
|
898
1138
|
def alter_table_add_column_sql(table, op)
|
|
899
1139
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
|
900
1140
|
end
|
|
@@ -1185,7 +1425,7 @@ module Sequel
|
|
|
1185
1425
|
elsif options[:foreign]
|
|
1186
1426
|
raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
|
|
1187
1427
|
'FOREIGN '
|
|
1188
|
-
elsif options[:
|
|
1428
|
+
elsif options.fetch(:unlogged){typecast_value_boolean(@opts[:unlogged_tables_default])}
|
|
1189
1429
|
'UNLOGGED '
|
|
1190
1430
|
end
|
|
1191
1431
|
|
|
@@ -1308,7 +1548,11 @@ module Sequel
|
|
|
1308
1548
|
# currently visible schemas.
|
|
1309
1549
|
def filter_schema(ds, opts)
|
|
1310
1550
|
expr = if schema = opts[:schema]
|
|
1311
|
-
schema.
|
|
1551
|
+
if schema.is_a?(SQL::Identifier)
|
|
1552
|
+
schema.value.to_s
|
|
1553
|
+
else
|
|
1554
|
+
schema.to_s
|
|
1555
|
+
end
|
|
1312
1556
|
else
|
|
1313
1557
|
Sequel.function(:any, Sequel.function(:current_schemas, false))
|
|
1314
1558
|
end
|
|
@@ -1399,11 +1643,12 @@ module Sequel
|
|
|
1399
1643
|
end
|
|
1400
1644
|
|
|
1401
1645
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
1402
|
-
# a rename table operation, so
|
|
1646
|
+
# a rename table operation, so specifying a new schema in new_name will not have an effect.
|
|
1403
1647
|
def rename_table_sql(name, new_name)
|
|
1404
1648
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
1405
1649
|
end
|
|
1406
1650
|
|
|
1651
|
+
# Handle interval and citext types.
|
|
1407
1652
|
def schema_column_type(db_type)
|
|
1408
1653
|
case db_type
|
|
1409
1654
|
when /\Ainterval\z/io
|
|
@@ -1415,43 +1660,48 @@ module Sequel
|
|
|
1415
1660
|
end
|
|
1416
1661
|
end
|
|
1417
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
|
|
1418
1700
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
1419
1701
|
def schema_parse_table(table_name, opts)
|
|
1420
1702
|
m = output_identifier_meth(opts[:dataset])
|
|
1421
|
-
oid = regclass_oid(table_name, opts)
|
|
1422
|
-
ds = metadata_dataset.select{[
|
|
1423
|
-
pg_attribute[:attname].as(:name),
|
|
1424
|
-
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
|
1425
|
-
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
|
1426
|
-
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
|
1427
|
-
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
|
1428
|
-
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
|
1429
|
-
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
|
1430
|
-
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
|
1431
|
-
from(:pg_class).
|
|
1432
|
-
join(:pg_attribute, :attrelid=>:oid).
|
|
1433
|
-
join(:pg_type, :oid=>:atttypid).
|
|
1434
|
-
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
|
1435
|
-
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
|
1436
|
-
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
|
1437
|
-
where{{pg_attribute[:attisdropped]=>false}}.
|
|
1438
|
-
where{pg_attribute[:attnum] > 0}.
|
|
1439
|
-
where{{pg_class[:oid]=>oid}}.
|
|
1440
|
-
order{pg_attribute[:attnum]}
|
|
1441
|
-
|
|
1442
|
-
# :nocov:
|
|
1443
|
-
if server_version > 100000
|
|
1444
|
-
# :nocov:
|
|
1445
|
-
ds = ds.select_append{pg_attribute[:attidentity]}
|
|
1446
|
-
|
|
1447
|
-
# :nocov:
|
|
1448
|
-
if server_version > 120000
|
|
1449
|
-
# :nocov:
|
|
1450
|
-
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
|
1451
|
-
end
|
|
1452
|
-
end
|
|
1453
1703
|
|
|
1454
|
-
|
|
1704
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
|
1455
1705
|
row[:default] = nil if blank_object?(row[:default])
|
|
1456
1706
|
if row[:base_oid]
|
|
1457
1707
|
row[:domain_oid] = row[:oid]
|
|
@@ -1462,11 +1712,33 @@ module Sequel
|
|
|
1462
1712
|
row.delete(:base_oid)
|
|
1463
1713
|
row.delete(:db_base_type)
|
|
1464
1714
|
end
|
|
1465
|
-
|
|
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
|
|
1466
1722
|
identity = row.delete(:attidentity)
|
|
1467
1723
|
if row[:primary_key]
|
|
1468
1724
|
row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
|
|
1469
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
|
+
|
|
1470
1742
|
[m.call(row.delete(:name)), row]
|
|
1471
1743
|
end
|
|
1472
1744
|
end
|
|
@@ -1548,7 +1820,7 @@ module Sequel
|
|
|
1548
1820
|
|
|
1549
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']])
|
|
1550
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']])
|
|
1551
|
-
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']])
|
|
1552
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']])
|
|
1553
1825
|
|
|
1554
1826
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
@@ -1574,8 +1846,6 @@ module Sequel
|
|
|
1574
1846
|
literal_append(sql, args[0])
|
|
1575
1847
|
sql << ' ' << op.to_s << ' '
|
|
1576
1848
|
literal_append(sql, args[1])
|
|
1577
|
-
sql << " ESCAPE "
|
|
1578
|
-
literal_append(sql, "\\")
|
|
1579
1849
|
sql << ')'
|
|
1580
1850
|
else
|
|
1581
1851
|
super
|
|
@@ -1600,6 +1870,12 @@ module Sequel
|
|
|
1600
1870
|
clone(:disable_insert_returning=>true)
|
|
1601
1871
|
end
|
|
1602
1872
|
|
|
1873
|
+
# Always return false when using VALUES
|
|
1874
|
+
def empty?
|
|
1875
|
+
return false if @opts[:values]
|
|
1876
|
+
super
|
|
1877
|
+
end
|
|
1878
|
+
|
|
1603
1879
|
# Return the results of an EXPLAIN query as a string
|
|
1604
1880
|
def explain(opts=OPTS)
|
|
1605
1881
|
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
|
@@ -1623,7 +1899,7 @@ module Sequel
|
|
|
1623
1899
|
# :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
|
|
1624
1900
|
# returned rows also include the exact phrase used.
|
|
1625
1901
|
# :rank :: Set to true to order by the rank, so that closer matches are returned first.
|
|
1626
|
-
# :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
|
|
1627
1903
|
# convert the terms to a ts_query.
|
|
1628
1904
|
# :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
|
|
1629
1905
|
# tsquery, and can be used directly in the query.
|
|
@@ -1643,6 +1919,8 @@ module Sequel
|
|
|
1643
1919
|
query_func = case to_tsquery = opts[:to_tsquery]
|
|
1644
1920
|
when :phrase, :plain
|
|
1645
1921
|
:"#{to_tsquery}to_tsquery"
|
|
1922
|
+
when :websearch
|
|
1923
|
+
:"websearch_to_tsquery"
|
|
1646
1924
|
else
|
|
1647
1925
|
(opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
|
|
1648
1926
|
end
|
|
@@ -1907,10 +2185,14 @@ module Sequel
|
|
|
1907
2185
|
server_version >= 90500
|
|
1908
2186
|
end
|
|
1909
2187
|
|
|
2188
|
+
# :nocov:
|
|
2189
|
+
|
|
1910
2190
|
# PostgreSQL supports timezones in literal timestamps
|
|
1911
2191
|
def supports_timestamp_timezones?
|
|
2192
|
+
# SEQUEL6: Remove
|
|
1912
2193
|
true
|
|
1913
2194
|
end
|
|
2195
|
+
# :nocov:
|
|
1914
2196
|
|
|
1915
2197
|
# PostgreSQL 8.4+ supports WINDOW clause.
|
|
1916
2198
|
def supports_window_clause?
|
|
@@ -1976,12 +2258,10 @@ module Sequel
|
|
|
1976
2258
|
# Otherwise, return an array of hashes.
|
|
1977
2259
|
def _import(columns, values, opts=OPTS)
|
|
1978
2260
|
if @opts[:returning]
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
statements.map{|st| returning_fetch_rows(st)}
|
|
1984
|
-
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}
|
|
1985
2265
|
elsif opts[:return] == :primary_key
|
|
1986
2266
|
returning(insert_pk)._import(columns, values, opts)
|
|
1987
2267
|
else
|
|
@@ -2021,12 +2301,22 @@ module Sequel
|
|
|
2021
2301
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
2022
2302
|
end
|
|
2023
2303
|
|
|
2304
|
+
# Use from_self for aggregate dataset using VALUES.
|
|
2305
|
+
def aggreate_dataset_use_from_self?
|
|
2306
|
+
super || @opts[:values]
|
|
2307
|
+
end
|
|
2308
|
+
|
|
2024
2309
|
# Allow truncation of multiple source tables.
|
|
2025
2310
|
def check_truncation_allowed!
|
|
2026
2311
|
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
|
2027
2312
|
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
|
2028
2313
|
end
|
|
2029
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
|
+
|
|
2030
2320
|
# Only include the primary table in the main delete clause
|
|
2031
2321
|
def delete_from_sql(sql)
|
|
2032
2322
|
sql << ' FROM '
|
|
@@ -2145,6 +2435,22 @@ module Sequel
|
|
|
2145
2435
|
end
|
|
2146
2436
|
end
|
|
2147
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
|
+
|
|
2148
2454
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
2149
2455
|
def literal_string_append(sql, v)
|
|
2150
2456
|
sql << "'" << v.gsub("'", "''") << "'"
|
|
@@ -2243,7 +2549,10 @@ module Sequel
|
|
|
2243
2549
|
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
|
2244
2550
|
def select_with_sql_cte(sql, cte)
|
|
2245
2551
|
super
|
|
2552
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
|
2553
|
+
end
|
|
2246
2554
|
|
|
2555
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
|
2247
2556
|
if search_opts = cte[:search]
|
|
2248
2557
|
sql << if search_opts[:type] == :breadth
|
|
2249
2558
|
" SEARCH BREADTH FIRST BY "
|