sequel 5.61.0 → 5.62.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +32 -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.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 +8 -2
- 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/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 +12 -0
- 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 +9 -10
- data/lib/sequel/extensions/pg_interval.rb +9 -10
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_multirange.rb +5 -10
- data/lib/sequel/extensions/pg_range.rb +5 -10
- 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 +1 -1
- metadata +10 -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]=>%w'r p',
|
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
|