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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +44 -0
  3. data/README.rdoc +20 -19
  4. data/doc/advanced_associations.rdoc +13 -13
  5. data/doc/association_basics.rdoc +21 -15
  6. data/doc/cheat_sheet.rdoc +3 -3
  7. data/doc/model_hooks.rdoc +1 -1
  8. data/doc/object_model.rdoc +8 -8
  9. data/doc/opening_databases.rdoc +4 -4
  10. data/doc/postgresql.rdoc +8 -8
  11. data/doc/querying.rdoc +1 -1
  12. data/doc/release_notes/5.61.0.txt +43 -0
  13. data/doc/release_notes/5.62.0.txt +132 -0
  14. data/doc/schema_modification.rdoc +1 -1
  15. data/doc/security.rdoc +9 -9
  16. data/doc/sql.rdoc +13 -13
  17. data/doc/testing.rdoc +13 -11
  18. data/doc/transactions.rdoc +6 -6
  19. data/doc/virtual_rows.rdoc +1 -1
  20. data/lib/sequel/adapters/postgres.rb +4 -0
  21. data/lib/sequel/adapters/shared/access.rb +9 -1
  22. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  23. data/lib/sequel/adapters/shared/mysql.rb +7 -0
  24. data/lib/sequel/adapters/shared/oracle.rb +7 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +275 -152
  26. data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
  27. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  28. data/lib/sequel/connection_pool.rb +42 -28
  29. data/lib/sequel/database/connecting.rb +24 -0
  30. data/lib/sequel/database/misc.rb +62 -12
  31. data/lib/sequel/database/query.rb +37 -0
  32. data/lib/sequel/dataset/actions.rb +31 -11
  33. data/lib/sequel/dataset/features.rb +5 -0
  34. data/lib/sequel/dataset/misc.rb +1 -1
  35. data/lib/sequel/dataset/query.rb +9 -9
  36. data/lib/sequel/dataset/sql.rb +5 -1
  37. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  38. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  39. data/lib/sequel/extensions/async_thread_pool.rb +11 -11
  40. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  41. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  42. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  43. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  44. data/lib/sequel/extensions/migration.rb +1 -1
  45. data/lib/sequel/extensions/named_timezones.rb +17 -5
  46. data/lib/sequel/extensions/pg_array.rb +22 -3
  47. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  48. data/lib/sequel/extensions/pg_extended_date_support.rb +27 -24
  49. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  50. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  51. data/lib/sequel/extensions/pg_inet.rb +10 -11
  52. data/lib/sequel/extensions/pg_interval.rb +10 -11
  53. data/lib/sequel/extensions/pg_json.rb +10 -10
  54. data/lib/sequel/extensions/pg_json_ops.rb +0 -52
  55. data/lib/sequel/extensions/pg_multirange.rb +5 -10
  56. data/lib/sequel/extensions/pg_range.rb +6 -11
  57. data/lib/sequel/extensions/pg_row.rb +18 -13
  58. data/lib/sequel/model/associations.rb +7 -2
  59. data/lib/sequel/model/base.rb +6 -5
  60. data/lib/sequel/plugins/auto_validations.rb +53 -15
  61. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  62. data/lib/sequel/plugins/composition.rb +2 -2
  63. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  64. data/lib/sequel/plugins/dirty.rb +1 -1
  65. data/lib/sequel/plugins/finder.rb +3 -1
  66. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  67. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
  68. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  69. data/lib/sequel/plugins/sql_comments.rb +1 -1
  70. data/lib/sequel/plugins/validation_helpers.rb +20 -0
  71. data/lib/sequel/version.rb +2 -2
  72. 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 = 140000
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
- rows.each do |row|
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
- ds.each do |row|
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
- oid = regclass_oid(table, opts)
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
- ds.each do |r|
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
- sql = "#{SELECT_PK_SQL} AND pg_class.oid = #{literal(regclass_oid(table, opts))}"
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
- sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
732
- if pks = fetch(sql).single_record
733
- value = literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
734
- Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
735
- else
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, :concurrently=>true)
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
- ds.map do |row|
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
- statements = multi_insert_sql(columns, values)
1980
- trans_opts = Hash[opts]
1981
- trans_opts[:server] = @opts[:server]
1982
- @db.transaction(trans_opts) do
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
- attr_accessor :after_connect
77
+ # Deprecated.
78
+ attr_reader :after_connect # SEQUEL6: Remove
78
79
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
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. The block is called
86
- # with a single symbol (specifying the server/shard to use) every time a new
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
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
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
- conn = @db.connect(server)
131
+ if @use_old_connect_api
132
+ # SEQUEL6: Remove block
133
+ conn = @db.connect(server)
123
134
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
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
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
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