sequel 5.60.1 → 5.62.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 +44 -0
- data/README.rdoc +20 -19
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +21 -15
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +4 -4
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +13 -13
- data/doc/testing.rdoc +13 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/postgres.rb +4 -0
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/mssql.rb +9 -5
- data/lib/sequel/adapters/shared/mysql.rb +7 -0
- data/lib/sequel/adapters/shared/oracle.rb +7 -0
- data/lib/sequel/adapters/shared/postgres.rb +275 -152
- data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
- data/lib/sequel/adapters/shared/sqlite.rb +5 -0
- data/lib/sequel/connection_pool.rb +42 -28
- data/lib/sequel/database/connecting.rb +24 -0
- data/lib/sequel/database/misc.rb +62 -12
- data/lib/sequel/database/query.rb +37 -0
- data/lib/sequel/dataset/actions.rb +31 -11
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/query.rb +9 -9
- data/lib/sequel/dataset/sql.rb +5 -1
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +11 -11
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/named_timezones.rb +17 -5
- data/lib/sequel/extensions/pg_array.rb +22 -3
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +27 -24
- 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 +0 -52
- data/lib/sequel/extensions/pg_multirange.rb +5 -10
- data/lib/sequel/extensions/pg_range.rb +6 -11
- data/lib/sequel/extensions/pg_row.rb +18 -13
- data/lib/sequel/model/associations.rb +7 -2
- data/lib/sequel/model/base.rb +6 -5
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/sql_comments.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +20 -0
- data/lib/sequel/version.rb +2 -2
- metadata +12 -5
@@ -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]
|
@@ -551,65 +559,12 @@ module Sequel
|
|
551
559
|
def foreign_key_list(table, opts=OPTS)
|
552
560
|
m = output_identifier_meth
|
553
561
|
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
562
|
|
609
563
|
h = {}
|
610
564
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
565
|
+
reverse = opts[:reverse]
|
611
566
|
|
612
|
-
|
567
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
613
568
|
if reverse
|
614
569
|
key = [row[:schema], row[:table], row[:name]]
|
615
570
|
else
|
@@ -644,6 +599,14 @@ module Sequel
|
|
644
599
|
def freeze
|
645
600
|
server_version
|
646
601
|
supports_prepared_transactions?
|
602
|
+
_schema_ds
|
603
|
+
_select_serial_sequence_ds
|
604
|
+
_select_custom_sequence_ds
|
605
|
+
_select_pk_ds
|
606
|
+
_indexes_ds
|
607
|
+
_check_constraints_ds
|
608
|
+
_foreign_key_list_ds
|
609
|
+
_reverse_foreign_key_list_ds
|
647
610
|
@conversion_procs.freeze
|
648
611
|
super
|
649
612
|
end
|
@@ -651,42 +614,11 @@ module Sequel
|
|
651
614
|
# Use the pg_* system tables to determine indexes on a table
|
652
615
|
def indexes(table, opts=OPTS)
|
653
616
|
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:
|
617
|
+
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
618
|
+
cond[:indpred] = nil unless opts[:include_partial]
|
687
619
|
|
688
620
|
indexes = {}
|
689
|
-
|
621
|
+
_indexes_ds.where_each(cond) do |r|
|
690
622
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
691
623
|
i[:columns] << m.call(r[:column])
|
692
624
|
end
|
@@ -719,8 +651,7 @@ module Sequel
|
|
719
651
|
def primary_key(table, opts=OPTS)
|
720
652
|
quoted_table = quote_schema_table(table)
|
721
653
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
722
|
-
|
723
|
-
value = fetch(sql).single_value
|
654
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
724
655
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
725
656
|
end
|
726
657
|
|
@@ -728,24 +659,21 @@ module Sequel
|
|
728
659
|
def primary_key_sequence(table, opts=OPTS)
|
729
660
|
quoted_table = quote_schema_table(table)
|
730
661
|
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
|
662
|
+
cond = {Sequel[:t][:oid] => regclass_oid(table, opts)}
|
663
|
+
value = if pks = _select_serial_sequence_ds.first(cond)
|
664
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
|
665
|
+
elsif pks = _select_custom_sequence_ds.first(cond)
|
666
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
741
667
|
end
|
668
|
+
|
669
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
742
670
|
end
|
743
671
|
|
744
672
|
# Refresh the materialized view with the given name.
|
745
673
|
#
|
746
674
|
# DB.refresh_view(:items_view)
|
747
675
|
# # REFRESH MATERIALIZED VIEW items_view
|
748
|
-
# DB.refresh_view(:items_view, :
|
676
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
749
677
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
750
678
|
def refresh_view(name, opts=OPTS)
|
751
679
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
@@ -895,6 +823,216 @@ module Sequel
|
|
895
823
|
|
896
824
|
private
|
897
825
|
|
826
|
+
# Dataset used to retrieve CHECK constraint information
|
827
|
+
def _check_constraints_ds
|
828
|
+
@_check_constraints_ds ||= metadata_dataset.
|
829
|
+
from{pg_constraint.as(:co)}.
|
830
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
831
|
+
where(:contype=>'c').
|
832
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
833
|
+
end
|
834
|
+
|
835
|
+
# Dataset used to retrieve foreign keys referenced by a table
|
836
|
+
def _foreign_key_list_ds
|
837
|
+
@_foreign_key_list_ds ||= __foreign_key_list_ds(false)
|
838
|
+
end
|
839
|
+
|
840
|
+
# Dataset used to retrieve foreign keys referencing a table
|
841
|
+
def _reverse_foreign_key_list_ds
|
842
|
+
@_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
|
843
|
+
end
|
844
|
+
|
845
|
+
# Build dataset used for foreign key list methods.
|
846
|
+
def __foreign_key_list_ds(reverse)
|
847
|
+
if reverse
|
848
|
+
ctable = Sequel[:att2]
|
849
|
+
cclass = Sequel[:cl2]
|
850
|
+
rtable = Sequel[:att]
|
851
|
+
rclass = Sequel[:cl]
|
852
|
+
else
|
853
|
+
ctable = Sequel[:att]
|
854
|
+
cclass = Sequel[:cl]
|
855
|
+
rtable = Sequel[:att2]
|
856
|
+
rclass = Sequel[:cl2]
|
857
|
+
end
|
858
|
+
|
859
|
+
if server_version >= 90500
|
860
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
861
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
862
|
+
# :nocov:
|
863
|
+
else
|
864
|
+
range = 0...32
|
865
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
866
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
867
|
+
# :nocov:
|
868
|
+
end
|
869
|
+
|
870
|
+
ds = metadata_dataset.
|
871
|
+
from{pg_constraint.as(:co)}.
|
872
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
873
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
874
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
875
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
876
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
877
|
+
order{[co[:conname], cpos]}.
|
878
|
+
where{{
|
879
|
+
cl[:relkind]=>%w'r p',
|
880
|
+
co[:contype]=>'f',
|
881
|
+
cpos=>rpos
|
882
|
+
}}.
|
883
|
+
select{[
|
884
|
+
co[:conname].as(:name),
|
885
|
+
ctable[:attname].as(:column),
|
886
|
+
co[:confupdtype].as(:on_update),
|
887
|
+
co[:confdeltype].as(:on_delete),
|
888
|
+
cl2[:relname].as(:table),
|
889
|
+
rtable[:attname].as(:refcolumn),
|
890
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
891
|
+
nsp[:nspname].as(:schema)
|
892
|
+
]}
|
893
|
+
|
894
|
+
if reverse
|
895
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
896
|
+
end
|
897
|
+
|
898
|
+
ds
|
899
|
+
end
|
900
|
+
|
901
|
+
# Dataset used to retrieve index information
|
902
|
+
def _indexes_ds
|
903
|
+
@_indexes_ds ||= begin
|
904
|
+
if server_version >= 90500
|
905
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
906
|
+
# :nocov:
|
907
|
+
else
|
908
|
+
range = 0...32
|
909
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
910
|
+
# :nocov:
|
911
|
+
end
|
912
|
+
|
913
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
914
|
+
|
915
|
+
ds = metadata_dataset.
|
916
|
+
from{pg_class.as(:tab)}.
|
917
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
918
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
919
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
920
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
921
|
+
where{{
|
922
|
+
indc[:relkind]=>'i',
|
923
|
+
ind[:indisprimary]=>false,
|
924
|
+
:indexprs=>nil,
|
925
|
+
:indisvalid=>true}}.
|
926
|
+
order(*order).
|
927
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
928
|
+
|
929
|
+
# :nocov:
|
930
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
931
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
932
|
+
# :nocov:
|
933
|
+
|
934
|
+
ds
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
# Dataset used to determine custom serial sequences for tables
|
939
|
+
def _select_custom_sequence_ds
|
940
|
+
@_select_custom_sequence_ds ||= metadata_dataset.
|
941
|
+
from{pg_class.as(:t)}.
|
942
|
+
join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
|
943
|
+
join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
|
944
|
+
join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
|
945
|
+
join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
|
946
|
+
where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
|
947
|
+
select{
|
948
|
+
expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
|
949
|
+
[
|
950
|
+
name[:nspname].as(:schema),
|
951
|
+
Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
|
952
|
+
]
|
953
|
+
}
|
954
|
+
end
|
955
|
+
|
956
|
+
# Dataset used to determine normal serial sequences for tables
|
957
|
+
def _select_serial_sequence_ds
|
958
|
+
@_serial_sequence_ds ||= metadata_dataset.
|
959
|
+
from{[
|
960
|
+
pg_class.as(:seq),
|
961
|
+
pg_attribute.as(:attr),
|
962
|
+
pg_depend.as(:dep),
|
963
|
+
pg_namespace.as(:name),
|
964
|
+
pg_constraint.as(:cons),
|
965
|
+
pg_class.as(:t)
|
966
|
+
]}.
|
967
|
+
where{[
|
968
|
+
[seq[:oid], dep[:objid]],
|
969
|
+
[seq[:relnamespace], name[:oid]],
|
970
|
+
[seq[:relkind], 'S'],
|
971
|
+
[attr[:attrelid], dep[:refobjid]],
|
972
|
+
[attr[:attnum], dep[:refobjsubid]],
|
973
|
+
[attr[:attrelid], cons[:conrelid]],
|
974
|
+
[attr[:attnum], cons[:conkey].sql_subscript(1)],
|
975
|
+
[attr[:attrelid], t[:oid]],
|
976
|
+
[cons[:contype], 'p']
|
977
|
+
]}.
|
978
|
+
select{[
|
979
|
+
name[:nspname].as(:schema),
|
980
|
+
seq[:relname].as(:sequence)
|
981
|
+
]}
|
982
|
+
end
|
983
|
+
|
984
|
+
# Dataset used to determine primary keys for tables
|
985
|
+
def _select_pk_ds
|
986
|
+
@_select_pk_ds ||= metadata_dataset.
|
987
|
+
from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
|
988
|
+
where{[
|
989
|
+
[pg_class[:oid], pg_attribute[:attrelid]],
|
990
|
+
[pg_class[:relnamespace], pg_namespace[:oid]],
|
991
|
+
[pg_class[:oid], pg_index[:indrelid]],
|
992
|
+
[pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
|
993
|
+
[pg_index[:indisprimary], 't']
|
994
|
+
]}.
|
995
|
+
select{pg_attribute[:attname].as(:pk)}
|
996
|
+
end
|
997
|
+
|
998
|
+
# Dataset used to get schema for tables
|
999
|
+
def _schema_ds
|
1000
|
+
@_schema_ds ||= begin
|
1001
|
+
ds = metadata_dataset.select{[
|
1002
|
+
pg_attribute[:attname].as(:name),
|
1003
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
1004
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
1005
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
1006
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
1007
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
1008
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
1009
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
1010
|
+
from(:pg_class).
|
1011
|
+
join(:pg_attribute, :attrelid=>:oid).
|
1012
|
+
join(:pg_type, :oid=>:atttypid).
|
1013
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
1014
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
1015
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
1016
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
1017
|
+
where{pg_attribute[:attnum] > 0}.
|
1018
|
+
order{pg_attribute[:attnum]}
|
1019
|
+
|
1020
|
+
# :nocov:
|
1021
|
+
if server_version > 100000
|
1022
|
+
# :nocov:
|
1023
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
1024
|
+
|
1025
|
+
# :nocov:
|
1026
|
+
if server_version > 120000
|
1027
|
+
# :nocov:
|
1028
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
ds
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
|
898
1036
|
def alter_table_add_column_sql(table, op)
|
899
1037
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
900
1038
|
end
|
@@ -1418,40 +1556,8 @@ module Sequel
|
|
1418
1556
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
1419
1557
|
def schema_parse_table(table_name, opts)
|
1420
1558
|
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
1559
|
|
1454
|
-
|
1560
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
1455
1561
|
row[:default] = nil if blank_object?(row[:default])
|
1456
1562
|
if row[:base_oid]
|
1457
1563
|
row[:domain_oid] = row[:oid]
|
@@ -1976,12 +2082,10 @@ module Sequel
|
|
1976
2082
|
# Otherwise, return an array of hashes.
|
1977
2083
|
def _import(columns, values, opts=OPTS)
|
1978
2084
|
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}
|
2085
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
2086
|
+
# that there's only ever a single statement.
|
2087
|
+
sql = multi_insert_sql(columns, values)[0]
|
2088
|
+
returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
|
1985
2089
|
elsif opts[:return] == :primary_key
|
1986
2090
|
returning(insert_pk)._import(columns, values, opts)
|
1987
2091
|
else
|
@@ -2145,6 +2249,22 @@ module Sequel
|
|
2145
2249
|
end
|
2146
2250
|
end
|
2147
2251
|
|
2252
|
+
# Handle Ruby integers outside PostgreSQL bigint range specially.
|
2253
|
+
def literal_integer(v)
|
2254
|
+
if v > 9223372036854775807 || v < -9223372036854775808
|
2255
|
+
literal_integer_outside_bigint_range(v)
|
2256
|
+
else
|
2257
|
+
v.to_s
|
2258
|
+
end
|
2259
|
+
end
|
2260
|
+
|
2261
|
+
# Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
|
2262
|
+
# outside PostgreSQL bigint range, so PostgreSQL doesn't treat
|
2263
|
+
# the value as numeric.
|
2264
|
+
def literal_integer_outside_bigint_range(v)
|
2265
|
+
raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
|
2266
|
+
end
|
2267
|
+
|
2148
2268
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
2149
2269
|
def literal_string_append(sql, v)
|
2150
2270
|
sql << "'" << v.gsub("'", "''") << "'"
|
@@ -2243,7 +2363,10 @@ module Sequel
|
|
2243
2363
|
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
2244
2364
|
def select_with_sql_cte(sql, cte)
|
2245
2365
|
super
|
2366
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
2367
|
+
end
|
2246
2368
|
|
2369
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
2247
2370
|
if search_opts = cte[:search]
|
2248
2371
|
sql << if search_opts[:type] == :breadth
|
2249
2372
|
" SEARCH BREADTH FIRST BY "
|
@@ -195,6 +195,11 @@ module Sequel
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
|
+
# SQLAnywhere tinyint types are unsigned.
|
199
|
+
def column_schema_tinyint_type_is_unsigned?
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
198
203
|
# SqlAnywhere doesn't support CREATE TABLE AS, it only supports SELECT INTO.
|
199
204
|
# Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
|
200
205
|
# is given as the argument, it can't work with a string, so raise an
|
@@ -213,6 +218,8 @@ module Sequel
|
|
213
218
|
def schema_column_type(db_type)
|
214
219
|
if convert_smallint_to_bool && db_type =~ /smallint/i
|
215
220
|
:boolean
|
221
|
+
elsif db_type =~ /unsigned (big)?int/i
|
222
|
+
:integer
|
216
223
|
else
|
217
224
|
super
|
218
225
|
end
|
@@ -320,6 +320,11 @@ module Sequel
|
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
323
|
+
# SQLite does not restrict the integer type to a specific range.
|
324
|
+
def column_schema_integer_min_max_values(db_type)
|
325
|
+
nil
|
326
|
+
end
|
327
|
+
|
323
328
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
324
329
|
# new connections.
|
325
330
|
def connection_pragmas
|
@@ -74,26 +74,35 @@ class Sequel::ConnectionPool
|
|
74
74
|
|
75
75
|
# The after_connect proc used for this pool. This is called with each new
|
76
76
|
# connection made, and is usually used to set custom per-connection settings.
|
77
|
-
|
77
|
+
# Deprecated.
|
78
|
+
attr_reader :after_connect # SEQUEL6: Remove
|
78
79
|
|
79
|
-
#
|
80
|
-
|
80
|
+
# Override the after_connect proc for the connection pool. Deprecated.
|
81
|
+
# Disables support for shard-specific :after_connect and :connect_sqls if used.
|
82
|
+
def after_connect=(v) # SEQUEL6: Remove
|
83
|
+
@use_old_connect_api = true
|
84
|
+
@after_connect = v
|
85
|
+
end
|
86
|
+
|
87
|
+
# An array of sql strings to execute on each new connection. Deprecated.
|
88
|
+
attr_reader :connect_sqls # SEQUEL6: Remove
|
89
|
+
|
90
|
+
# Override the connect_sqls for the connection pool. Deprecated.
|
91
|
+
# Disables support for shard-specific :after_connect and :connect_sqls if used.
|
92
|
+
def connect_sqls=(v) # SEQUEL6: Remove
|
93
|
+
@use_old_connect_api = true
|
94
|
+
@connect_sqls = v
|
95
|
+
end
|
81
96
|
|
82
97
|
# The Sequel::Database object tied to this connection pool.
|
83
98
|
attr_accessor :db
|
84
99
|
|
85
|
-
# Instantiates a connection pool with the given options.
|
86
|
-
|
87
|
-
# connection is needed. The following options are respected for all connection
|
88
|
-
# pools:
|
89
|
-
# :after_connect :: A callable object called after each new connection is made, with the
|
90
|
-
# connection object (and server argument if the callable accepts 2 arguments),
|
91
|
-
# useful for customizations that you want to apply to all connections.
|
92
|
-
# :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
|
93
|
-
def initialize(db, opts=OPTS)
|
100
|
+
# Instantiates a connection pool with the given Database and options.
|
101
|
+
def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
|
94
102
|
@db = db
|
95
|
-
@
|
96
|
-
@
|
103
|
+
@use_old_connect_api = false # SEQUEL6: Remove
|
104
|
+
@after_connect = opts[:after_connect] # SEQUEL6: Remove
|
105
|
+
@connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
|
97
106
|
@error_classes = db.send(:database_error_classes).dup.freeze
|
98
107
|
end
|
99
108
|
|
@@ -119,25 +128,30 @@ class Sequel::ConnectionPool
|
|
119
128
|
# and checking for connection errors.
|
120
129
|
def make_new(server)
|
121
130
|
begin
|
122
|
-
|
131
|
+
if @use_old_connect_api
|
132
|
+
# SEQUEL6: Remove block
|
133
|
+
conn = @db.connect(server)
|
123
134
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
135
|
+
if ac = @after_connect
|
136
|
+
if ac.arity == 2
|
137
|
+
ac.call(conn, server)
|
138
|
+
else
|
139
|
+
ac.call(conn)
|
140
|
+
end
|
129
141
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
142
|
+
|
143
|
+
if cs = @connect_sqls
|
144
|
+
cs.each do |sql|
|
145
|
+
db.send(:log_connection_execute, conn, sql)
|
146
|
+
end
|
135
147
|
end
|
148
|
+
|
149
|
+
conn
|
150
|
+
else
|
151
|
+
@db.new_connection(server)
|
136
152
|
end
|
137
153
|
rescue Exception=>exception
|
138
154
|
raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
|
139
|
-
end
|
140
|
-
raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
|
141
|
-
conn
|
155
|
+
end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
|
142
156
|
end
|
143
157
|
end
|