sequel 5.61.0 → 5.62.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -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.62.0.txt +132 -0
  13. data/doc/schema_modification.rdoc +1 -1
  14. data/doc/security.rdoc +9 -9
  15. data/doc/sql.rdoc +13 -13
  16. data/doc/testing.rdoc +13 -11
  17. data/doc/transactions.rdoc +6 -6
  18. data/doc/virtual_rows.rdoc +1 -1
  19. data/lib/sequel/adapters/postgres.rb +4 -0
  20. data/lib/sequel/adapters/shared/access.rb +9 -1
  21. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  22. data/lib/sequel/adapters/shared/mysql.rb +7 -0
  23. data/lib/sequel/adapters/shared/oracle.rb +7 -0
  24. data/lib/sequel/adapters/shared/postgres.rb +275 -152
  25. data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
  26. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  27. data/lib/sequel/connection_pool.rb +42 -28
  28. data/lib/sequel/database/connecting.rb +24 -0
  29. data/lib/sequel/database/misc.rb +8 -2
  30. data/lib/sequel/database/query.rb +37 -0
  31. data/lib/sequel/dataset/actions.rb +31 -11
  32. data/lib/sequel/dataset/features.rb +5 -0
  33. data/lib/sequel/dataset/misc.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +9 -9
  35. data/lib/sequel/dataset/sql.rb +5 -1
  36. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  37. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  38. data/lib/sequel/extensions/async_thread_pool.rb +11 -11
  39. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  40. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  41. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  42. data/lib/sequel/extensions/migration.rb +1 -1
  43. data/lib/sequel/extensions/named_timezones.rb +17 -5
  44. data/lib/sequel/extensions/pg_array.rb +22 -3
  45. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  46. data/lib/sequel/extensions/pg_extended_date_support.rb +12 -0
  47. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  48. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  49. data/lib/sequel/extensions/pg_inet.rb +9 -10
  50. data/lib/sequel/extensions/pg_interval.rb +9 -10
  51. data/lib/sequel/extensions/pg_json.rb +10 -10
  52. data/lib/sequel/extensions/pg_multirange.rb +5 -10
  53. data/lib/sequel/extensions/pg_range.rb +5 -10
  54. data/lib/sequel/extensions/pg_row.rb +18 -13
  55. data/lib/sequel/model/associations.rb +7 -2
  56. data/lib/sequel/model/base.rb +6 -5
  57. data/lib/sequel/plugins/auto_validations.rb +53 -15
  58. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  59. data/lib/sequel/plugins/composition.rb +2 -2
  60. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  61. data/lib/sequel/plugins/dirty.rb +1 -1
  62. data/lib/sequel/plugins/finder.rb +3 -1
  63. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  64. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
  65. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  66. data/lib/sequel/plugins/sql_comments.rb +1 -1
  67. data/lib/sequel/plugins/validation_helpers.rb +20 -0
  68. data/lib/sequel/version.rb +1 -1
  69. 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 = 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]=>%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
- 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