sequel 5.60.1 → 5.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +280 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +27 -24
  5. data/doc/advanced_associations.rdoc +16 -14
  6. data/doc/association_basics.rdoc +53 -17
  7. data/doc/cheat_sheet.rdoc +3 -3
  8. data/doc/code_order.rdoc +5 -3
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/mass_assignment.rdoc +1 -1
  11. data/doc/migration.rdoc +15 -0
  12. data/doc/model_hooks.rdoc +1 -1
  13. data/doc/object_model.rdoc +8 -8
  14. data/doc/opening_databases.rdoc +18 -7
  15. data/doc/postgresql.rdoc +8 -8
  16. data/doc/querying.rdoc +7 -2
  17. data/doc/release_notes/5.61.0.txt +43 -0
  18. data/doc/release_notes/5.62.0.txt +132 -0
  19. data/doc/release_notes/5.63.0.txt +33 -0
  20. data/doc/release_notes/5.64.0.txt +50 -0
  21. data/doc/release_notes/5.65.0.txt +21 -0
  22. data/doc/release_notes/5.66.0.txt +24 -0
  23. data/doc/release_notes/5.67.0.txt +32 -0
  24. data/doc/release_notes/5.68.0.txt +61 -0
  25. data/doc/release_notes/5.69.0.txt +26 -0
  26. data/doc/release_notes/5.70.0.txt +35 -0
  27. data/doc/release_notes/5.71.0.txt +21 -0
  28. data/doc/release_notes/5.72.0.txt +33 -0
  29. data/doc/release_notes/5.73.0.txt +66 -0
  30. data/doc/release_notes/5.74.0.txt +45 -0
  31. data/doc/release_notes/5.75.0.txt +35 -0
  32. data/doc/release_notes/5.76.0.txt +86 -0
  33. data/doc/release_notes/5.77.0.txt +63 -0
  34. data/doc/release_notes/5.78.0.txt +67 -0
  35. data/doc/release_notes/5.79.0.txt +28 -0
  36. data/doc/release_notes/5.80.0.txt +40 -0
  37. data/doc/release_notes/5.81.0.txt +31 -0
  38. data/doc/schema_modification.rdoc +3 -3
  39. data/doc/security.rdoc +9 -9
  40. data/doc/sharding.rdoc +3 -1
  41. data/doc/sql.rdoc +14 -14
  42. data/doc/testing.rdoc +16 -12
  43. data/doc/transactions.rdoc +6 -6
  44. data/doc/virtual_rows.rdoc +1 -1
  45. data/lib/sequel/adapters/ibmdb.rb +1 -1
  46. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  47. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  48. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  49. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  50. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  51. data/lib/sequel/adapters/jdbc.rb +10 -6
  52. data/lib/sequel/adapters/mysql.rb +19 -7
  53. data/lib/sequel/adapters/mysql2.rb +2 -2
  54. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  55. data/lib/sequel/adapters/oracle.rb +1 -0
  56. data/lib/sequel/adapters/postgres.rb +10 -5
  57. data/lib/sequel/adapters/shared/access.rb +9 -1
  58. data/lib/sequel/adapters/shared/db2.rb +12 -0
  59. data/lib/sequel/adapters/shared/mssql.rb +71 -9
  60. data/lib/sequel/adapters/shared/mysql.rb +82 -3
  61. data/lib/sequel/adapters/shared/oracle.rb +17 -7
  62. data/lib/sequel/adapters/shared/postgres.rb +469 -160
  63. data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
  64. data/lib/sequel/adapters/shared/sqlite.rb +39 -3
  65. data/lib/sequel/adapters/sqlite.rb +42 -3
  66. data/lib/sequel/adapters/trilogy.rb +117 -0
  67. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  68. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  69. data/lib/sequel/connection_pool/threaded.rb +14 -8
  70. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  71. data/lib/sequel/connection_pool.rb +57 -31
  72. data/lib/sequel/database/connecting.rb +25 -1
  73. data/lib/sequel/database/dataset.rb +16 -6
  74. data/lib/sequel/database/misc.rb +65 -14
  75. data/lib/sequel/database/query.rb +72 -1
  76. data/lib/sequel/database/schema_generator.rb +1 -1
  77. data/lib/sequel/database/schema_methods.rb +12 -5
  78. data/lib/sequel/database/transactions.rb +6 -0
  79. data/lib/sequel/dataset/actions.rb +60 -13
  80. data/lib/sequel/dataset/dataset_module.rb +1 -1
  81. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  82. data/lib/sequel/dataset/features.rb +15 -1
  83. data/lib/sequel/dataset/graph.rb +1 -0
  84. data/lib/sequel/dataset/misc.rb +12 -2
  85. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  86. data/lib/sequel/dataset/query.rb +119 -45
  87. data/lib/sequel/dataset/sql.rb +58 -36
  88. data/lib/sequel/dataset.rb +4 -0
  89. data/lib/sequel/exceptions.rb +5 -0
  90. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  91. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  92. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  93. data/lib/sequel/extensions/async_thread_pool.rb +21 -13
  94. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  95. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  96. data/lib/sequel/extensions/caller_logging.rb +4 -1
  97. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  98. data/lib/sequel/extensions/connection_validator.rb +16 -11
  99. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  100. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  101. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  102. data/lib/sequel/extensions/index_caching.rb +5 -1
  103. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  104. data/lib/sequel/extensions/migration.rb +65 -15
  105. data/lib/sequel/extensions/named_timezones.rb +22 -6
  106. data/lib/sequel/extensions/pg_array.rb +33 -4
  107. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  108. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  109. data/lib/sequel/extensions/pg_enum.rb +1 -2
  110. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  111. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  112. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  113. data/lib/sequel/extensions/pg_inet.rb +10 -11
  114. data/lib/sequel/extensions/pg_interval.rb +10 -11
  115. data/lib/sequel/extensions/pg_json.rb +10 -10
  116. data/lib/sequel/extensions/pg_json_ops.rb +3 -3
  117. data/lib/sequel/extensions/pg_multirange.rb +6 -11
  118. data/lib/sequel/extensions/pg_range.rb +9 -14
  119. data/lib/sequel/extensions/pg_row.rb +20 -19
  120. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  121. data/lib/sequel/extensions/provenance.rb +110 -0
  122. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  123. data/lib/sequel/extensions/schema_caching.rb +1 -1
  124. data/lib/sequel/extensions/schema_dumper.rb +32 -9
  125. data/lib/sequel/extensions/server_block.rb +2 -1
  126. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  127. data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
  128. data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
  129. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  130. data/lib/sequel/model/associations.rb +38 -11
  131. data/lib/sequel/model/base.rb +32 -18
  132. data/lib/sequel/model/dataset_module.rb +3 -0
  133. data/lib/sequel/model/exceptions.rb +15 -3
  134. data/lib/sequel/plugins/auto_validations.rb +53 -15
  135. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  136. data/lib/sequel/plugins/column_encryption.rb +28 -7
  137. data/lib/sequel/plugins/composition.rb +2 -2
  138. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  139. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  140. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  141. data/lib/sequel/plugins/dirty.rb +1 -1
  142. data/lib/sequel/plugins/finder.rb +4 -2
  143. data/lib/sequel/plugins/input_transformer.rb +1 -1
  144. data/lib/sequel/plugins/list.rb +5 -2
  145. data/lib/sequel/plugins/many_through_many.rb +1 -1
  146. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  147. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  148. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  149. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  150. data/lib/sequel/plugins/paged_operations.rb +181 -0
  151. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  152. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  153. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  154. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  155. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  156. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  157. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  158. data/lib/sequel/plugins/sql_comments.rb +1 -1
  159. data/lib/sequel/plugins/static_cache.rb +38 -0
  160. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  161. data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
  162. data/lib/sequel/plugins/validate_associated.rb +22 -12
  163. data/lib/sequel/plugins/validation_helpers.rb +29 -2
  164. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  165. data/lib/sequel/version.rb +2 -2
  166. metadata +79 -6
@@ -19,6 +19,9 @@ module Sequel
19
19
  module Postgres
20
20
  Sequel::Database.set_shared_adapter_scheme(:postgres, self)
21
21
 
22
+ # Exception class ranged when literalizing integers outside the bigint/int8 range.
23
+ class IntegerOutsideBigintRange < InvalidValue; end
24
+
22
25
  NAN = 0.0/0.0
23
26
  PLUS_INFINITY = 1.0/0.0
24
27
  MINUS_INFINITY = -1.0/0.0
@@ -83,11 +86,22 @@ module Sequel
83
86
  def primary_key(table)
84
87
  :id
85
88
  end
89
+
90
+ private
91
+
92
+ # Handle NoMethodErrors when parsing schema due to output_identifier
93
+ # being called with nil when the Database fetch results are not set
94
+ # to what schema parsing expects.
95
+ def schema_parse_table(table, opts=OPTS)
96
+ super
97
+ rescue NoMethodError
98
+ []
99
+ end
86
100
  end
87
101
 
88
102
  def self.mock_adapter_setup(db)
89
103
  db.instance_exec do
90
- @server_version = 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]
@@ -490,6 +498,25 @@ module Sequel
490
498
  :postgres
491
499
  end
492
500
 
501
+ # For constraints that are deferrable, defer constraints until
502
+ # transaction commit. Options:
503
+ #
504
+ # :constraints :: An identifier of the constraint, or an array of
505
+ # identifiers for constraints, to apply this
506
+ # change to specific constraints.
507
+ # :server :: The server/shard on which to run the query.
508
+ #
509
+ # Examples:
510
+ #
511
+ # DB.defer_constraints
512
+ # # SET CONSTRAINTS ALL DEFERRED
513
+ #
514
+ # DB.defer_constraints(constraints: [:c1, Sequel[:sc][:c2]])
515
+ # # SET CONSTRAINTS "c1", "sc"."s2" DEFERRED
516
+ def defer_constraints(opts=OPTS)
517
+ _set_constraints(' DEFERRED', opts)
518
+ end
519
+
493
520
  # Use PostgreSQL's DO syntax to execute an anonymous code block. The code should
494
521
  # be the literal code string to use in the underlying procedural language. Options:
495
522
  #
@@ -551,65 +578,12 @@ module Sequel
551
578
  def foreign_key_list(table, opts=OPTS)
552
579
  m = output_identifier_meth
553
580
  schema, _ = opts.fetch(:schema, schema_and_table(table))
554
- oid = regclass_oid(table)
555
- reverse = opts[:reverse]
556
-
557
- if reverse
558
- ctable = Sequel[:att2]
559
- cclass = Sequel[:cl2]
560
- rtable = Sequel[:att]
561
- rclass = Sequel[:cl]
562
- else
563
- ctable = Sequel[:att]
564
- cclass = Sequel[:cl]
565
- rtable = Sequel[:att2]
566
- rclass = Sequel[:cl2]
567
- end
568
-
569
- if server_version >= 90500
570
- cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
571
- rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
572
- # :nocov:
573
- else
574
- range = 0...32
575
- cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
576
- rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
577
- # :nocov:
578
- end
579
-
580
- ds = metadata_dataset.
581
- from{pg_constraint.as(:co)}.
582
- join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
583
- join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
584
- join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
585
- join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
586
- join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
587
- order{[co[:conname], cpos]}.
588
- where{{
589
- cl[:relkind]=>'r',
590
- co[:contype]=>'f',
591
- cl[:oid]=>oid,
592
- cpos=>rpos
593
- }}.
594
- select{[
595
- co[:conname].as(:name),
596
- ctable[:attname].as(:column),
597
- co[:confupdtype].as(:on_update),
598
- co[:confdeltype].as(:on_delete),
599
- cl2[:relname].as(:table),
600
- rtable[:attname].as(:refcolumn),
601
- SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
602
- nsp[:nspname].as(:schema)
603
- ]}
604
-
605
- if reverse
606
- ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
607
- end
608
581
 
609
582
  h = {}
610
583
  fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
584
+ reverse = opts[:reverse]
611
585
 
612
- ds.each do |row|
586
+ (reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
613
587
  if reverse
614
588
  key = [row[:schema], row[:table], row[:name]]
615
589
  else
@@ -644,49 +618,44 @@ module Sequel
644
618
  def freeze
645
619
  server_version
646
620
  supports_prepared_transactions?
621
+ _schema_ds
622
+ _select_serial_sequence_ds
623
+ _select_custom_sequence_ds
624
+ _select_pk_ds
625
+ _indexes_ds
626
+ _check_constraints_ds
627
+ _foreign_key_list_ds
628
+ _reverse_foreign_key_list_ds
647
629
  @conversion_procs.freeze
648
630
  super
649
631
  end
650
632
 
633
+ # Immediately apply deferrable constraints.
634
+ #
635
+ # :constraints :: An identifier of the constraint, or an array of
636
+ # identifiers for constraints, to apply this
637
+ # change to specific constraints.
638
+ # :server :: The server/shard on which to run the query.
639
+ #
640
+ # Examples:
641
+ #
642
+ # DB.immediate_constraints
643
+ # # SET CONSTRAINTS ALL IMMEDIATE
644
+ #
645
+ # DB.immediate_constraints(constraints: [:c1, Sequel[:sc][:c2]])
646
+ # # SET CONSTRAINTS "c1", "sc"."s2" IMMEDIATE
647
+ def immediate_constraints(opts=OPTS)
648
+ _set_constraints(' IMMEDIATE', opts)
649
+ end
650
+
651
651
  # Use the pg_* system tables to determine indexes on a table
652
652
  def indexes(table, opts=OPTS)
653
653
  m = output_identifier_meth
654
- 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:
654
+ cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
655
+ cond[:indpred] = nil unless opts[:include_partial]
687
656
 
688
657
  indexes = {}
689
- ds.each do |r|
658
+ _indexes_ds.where_each(cond) do |r|
690
659
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
691
660
  i[:columns] << m.call(r[:column])
692
661
  end
@@ -719,8 +688,7 @@ module Sequel
719
688
  def primary_key(table, opts=OPTS)
720
689
  quoted_table = quote_schema_table(table)
721
690
  Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
722
- sql = "#{SELECT_PK_SQL} AND pg_class.oid = #{literal(regclass_oid(table, opts))}"
723
- value = fetch(sql).single_value
691
+ value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
724
692
  Sequel.synchronize{@primary_keys[quoted_table] = value}
725
693
  end
726
694
 
@@ -728,24 +696,21 @@ module Sequel
728
696
  def primary_key_sequence(table, opts=OPTS)
729
697
  quoted_table = quote_schema_table(table)
730
698
  Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
731
- 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
699
+ cond = {Sequel[:t][:oid] => regclass_oid(table, opts)}
700
+ value = if pks = _select_serial_sequence_ds.first(cond)
701
+ literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
702
+ elsif pks = _select_custom_sequence_ds.first(cond)
703
+ literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
741
704
  end
705
+
706
+ Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
742
707
  end
743
708
 
744
709
  # Refresh the materialized view with the given name.
745
710
  #
746
711
  # DB.refresh_view(:items_view)
747
712
  # # REFRESH MATERIALIZED VIEW items_view
748
- # DB.refresh_view(:items_view, :concurrently=>true)
713
+ # DB.refresh_view(:items_view, concurrently: true)
749
714
  # # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
750
715
  def refresh_view(name, opts=OPTS)
751
716
  run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
@@ -877,6 +842,7 @@ module Sequel
877
842
  # DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
878
843
  # # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
879
844
  def values(v)
845
+ raise Error, "Cannot provide an empty array for values" if v.empty?
880
846
  @default_dataset.clone(:values=>v)
881
847
  end
882
848
 
@@ -893,8 +859,282 @@ module Sequel
893
859
  pg_class_relname(relkind, opts)
894
860
  end
895
861
 
862
+ # Attempt to acquire an exclusive advisory lock with the given lock_id (which should be
863
+ # a 64-bit integer). If successful, yield to the block, then release the advisory lock
864
+ # when the block exits. If unsuccessful, raise a Sequel::AdvisoryLockError.
865
+ #
866
+ # DB.with_advisory_lock(1347){DB.get(1)}
867
+ # # SELECT pg_try_advisory_lock(1357) LIMIT 1
868
+ # # SELECT 1 AS v LIMIT 1
869
+ # # SELECT pg_advisory_unlock(1357) LIMIT 1
870
+ #
871
+ # Options:
872
+ # :wait :: Do not raise an error, instead, wait until the advisory lock can be acquired.
873
+ def with_advisory_lock(lock_id, opts=OPTS)
874
+ ds = dataset
875
+ if server = opts[:server]
876
+ ds = ds.server(server)
877
+ end
878
+
879
+ synchronize(server) do |c|
880
+ begin
881
+ if opts[:wait]
882
+ ds.get{pg_advisory_lock(lock_id)}
883
+ locked = true
884
+ else
885
+ unless locked = ds.get{pg_try_advisory_lock(lock_id)}
886
+ raise AdvisoryLockError, "unable to acquire advisory lock #{lock_id.inspect}"
887
+ end
888
+ end
889
+
890
+ yield
891
+ ensure
892
+ ds.get{pg_advisory_unlock(lock_id)} if locked
893
+ end
894
+ end
895
+ end
896
+
896
897
  private
897
898
 
899
+ # Dataset used to retrieve CHECK constraint information
900
+ def _check_constraints_ds
901
+ @_check_constraints_ds ||= metadata_dataset.
902
+ from{pg_constraint.as(:co)}.
903
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
904
+ where(:contype=>'c').
905
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
906
+ end
907
+
908
+ # Dataset used to retrieve foreign keys referenced by a table
909
+ def _foreign_key_list_ds
910
+ @_foreign_key_list_ds ||= __foreign_key_list_ds(false)
911
+ end
912
+
913
+ # Dataset used to retrieve foreign keys referencing a table
914
+ def _reverse_foreign_key_list_ds
915
+ @_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
916
+ end
917
+
918
+ # Build dataset used for foreign key list methods.
919
+ def __foreign_key_list_ds(reverse)
920
+ if reverse
921
+ ctable = Sequel[:att2]
922
+ cclass = Sequel[:cl2]
923
+ rtable = Sequel[:att]
924
+ rclass = Sequel[:cl]
925
+ else
926
+ ctable = Sequel[:att]
927
+ cclass = Sequel[:cl]
928
+ rtable = Sequel[:att2]
929
+ rclass = Sequel[:cl2]
930
+ end
931
+
932
+ if server_version >= 90500
933
+ cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
934
+ rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
935
+ # :nocov:
936
+ else
937
+ range = 0...32
938
+ cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
939
+ rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
940
+ # :nocov:
941
+ end
942
+
943
+ ds = metadata_dataset.
944
+ from{pg_constraint.as(:co)}.
945
+ join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
946
+ join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
947
+ join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
948
+ join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
949
+ join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
950
+ order{[co[:conname], cpos]}.
951
+ where{{
952
+ cl[:relkind]=>%w'r p',
953
+ co[:contype]=>'f',
954
+ cpos=>rpos
955
+ }}.
956
+ select{[
957
+ co[:conname].as(:name),
958
+ ctable[:attname].as(:column),
959
+ co[:confupdtype].as(:on_update),
960
+ co[:confdeltype].as(:on_delete),
961
+ cl2[:relname].as(:table),
962
+ rtable[:attname].as(:refcolumn),
963
+ SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
964
+ nsp[:nspname].as(:schema)
965
+ ]}
966
+
967
+ if reverse
968
+ ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
969
+ end
970
+
971
+ ds
972
+ end
973
+
974
+ # Dataset used to retrieve index information
975
+ def _indexes_ds
976
+ @_indexes_ds ||= begin
977
+ if server_version >= 90500
978
+ order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
979
+ # :nocov:
980
+ else
981
+ range = 0...32
982
+ order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
983
+ # :nocov:
984
+ end
985
+
986
+ attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
987
+
988
+ ds = metadata_dataset.
989
+ from{pg_class.as(:tab)}.
990
+ join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
991
+ join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
992
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
993
+ left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
994
+ where{{
995
+ indc[:relkind]=>%w'i I',
996
+ ind[:indisprimary]=>false,
997
+ :indexprs=>nil,
998
+ :indisvalid=>true}}.
999
+ order(*order).
1000
+ select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
1001
+
1002
+ # :nocov:
1003
+ ds = ds.where(:indisready=>true) if server_version >= 80300
1004
+ ds = ds.where(:indislive=>true) if server_version >= 90300
1005
+ # :nocov:
1006
+
1007
+ ds
1008
+ end
1009
+ end
1010
+
1011
+ # Dataset used to determine custom serial sequences for tables
1012
+ def _select_custom_sequence_ds
1013
+ @_select_custom_sequence_ds ||= metadata_dataset.
1014
+ from{pg_class.as(:t)}.
1015
+ join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
1016
+ join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
1017
+ join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
1018
+ join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
1019
+ where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
1020
+ select{
1021
+ expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
1022
+ [
1023
+ name[:nspname].as(:schema),
1024
+ Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
1025
+ ]
1026
+ }
1027
+ end
1028
+
1029
+ # Dataset used to determine normal serial sequences for tables
1030
+ def _select_serial_sequence_ds
1031
+ @_serial_sequence_ds ||= metadata_dataset.
1032
+ from{[
1033
+ pg_class.as(:seq),
1034
+ pg_attribute.as(:attr),
1035
+ pg_depend.as(:dep),
1036
+ pg_namespace.as(:name),
1037
+ pg_constraint.as(:cons),
1038
+ pg_class.as(:t)
1039
+ ]}.
1040
+ where{[
1041
+ [seq[:oid], dep[:objid]],
1042
+ [seq[:relnamespace], name[:oid]],
1043
+ [seq[:relkind], 'S'],
1044
+ [attr[:attrelid], dep[:refobjid]],
1045
+ [attr[:attnum], dep[:refobjsubid]],
1046
+ [attr[:attrelid], cons[:conrelid]],
1047
+ [attr[:attnum], cons[:conkey].sql_subscript(1)],
1048
+ [attr[:attrelid], t[:oid]],
1049
+ [cons[:contype], 'p']
1050
+ ]}.
1051
+ select{[
1052
+ name[:nspname].as(:schema),
1053
+ seq[:relname].as(:sequence)
1054
+ ]}
1055
+ end
1056
+
1057
+ # Dataset used to determine primary keys for tables
1058
+ def _select_pk_ds
1059
+ @_select_pk_ds ||= metadata_dataset.
1060
+ from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
1061
+ where{[
1062
+ [pg_class[:oid], pg_attribute[:attrelid]],
1063
+ [pg_class[:relnamespace], pg_namespace[:oid]],
1064
+ [pg_class[:oid], pg_index[:indrelid]],
1065
+ [pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
1066
+ [pg_index[:indisprimary], 't']
1067
+ ]}.
1068
+ select{pg_attribute[:attname].as(:pk)}
1069
+ end
1070
+
1071
+ # Dataset used to get schema for tables
1072
+ def _schema_ds
1073
+ @_schema_ds ||= begin
1074
+ ds = metadata_dataset.select{[
1075
+ pg_attribute[:attname].as(:name),
1076
+ SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1077
+ SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1078
+ SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1079
+ SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1080
+ SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1081
+ SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1082
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key),
1083
+ Sequel[:pg_type][:typtype],
1084
+ (~Sequel[Sequel[:elementtype][:oid]=>nil]).as(:is_array),
1085
+ ]}.
1086
+ from(:pg_class).
1087
+ join(:pg_attribute, :attrelid=>:oid).
1088
+ join(:pg_type, :oid=>:atttypid).
1089
+ left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1090
+ left_outer_join(Sequel[:pg_type].as(:elementtype), :typarray=>Sequel[:pg_type][:oid]).
1091
+ left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1092
+ left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1093
+ where{{pg_attribute[:attisdropped]=>false}}.
1094
+ where{pg_attribute[:attnum] > 0}.
1095
+ order{pg_attribute[:attnum]}
1096
+
1097
+ # :nocov:
1098
+ if server_version > 100000
1099
+ # :nocov:
1100
+ ds = ds.select_append{pg_attribute[:attidentity]}
1101
+
1102
+ # :nocov:
1103
+ if server_version > 120000
1104
+ # :nocov:
1105
+ ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1106
+ end
1107
+ end
1108
+
1109
+ ds
1110
+ end
1111
+ end
1112
+
1113
+ # Internals of defer_constraints/immediate_constraints
1114
+ def _set_constraints(type, opts)
1115
+ execute_ddl(_set_constraints_sql(type, opts), opts)
1116
+ end
1117
+
1118
+ # SQL to use for SET CONSTRAINTS
1119
+ def _set_constraints_sql(type, opts)
1120
+ sql = String.new
1121
+ sql << "SET CONSTRAINTS "
1122
+ if constraints = opts[:constraints]
1123
+ dataset.send(:source_list_append, sql, Array(constraints))
1124
+ else
1125
+ sql << "ALL"
1126
+ end
1127
+ sql << type
1128
+ end
1129
+
1130
+ # Consider lock or statement timeout errors as evidence that the table exists
1131
+ # but is locked.
1132
+ def _table_exists?(ds)
1133
+ super
1134
+ rescue DatabaseError => e
1135
+ raise e unless /canceling statement due to (?:statement|lock) timeout/ =~ e.message
1136
+ end
1137
+
898
1138
  def alter_table_add_column_sql(table, op)
899
1139
  "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
900
1140
  end
@@ -1185,7 +1425,7 @@ module Sequel
1185
1425
  elsif options[:foreign]
1186
1426
  raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
1187
1427
  'FOREIGN '
1188
- elsif options[:unlogged]
1428
+ elsif options.fetch(:unlogged){typecast_value_boolean(@opts[:unlogged_tables_default])}
1189
1429
  'UNLOGGED '
1190
1430
  end
1191
1431
 
@@ -1308,7 +1548,11 @@ module Sequel
1308
1548
  # currently visible schemas.
1309
1549
  def filter_schema(ds, opts)
1310
1550
  expr = if schema = opts[:schema]
1311
- schema.to_s
1551
+ if schema.is_a?(SQL::Identifier)
1552
+ schema.value.to_s
1553
+ else
1554
+ schema.to_s
1555
+ end
1312
1556
  else
1313
1557
  Sequel.function(:any, Sequel.function(:current_schemas, false))
1314
1558
  end
@@ -1399,11 +1643,12 @@ module Sequel
1399
1643
  end
1400
1644
 
1401
1645
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
1402
- # a rename table operation, so speciying a new schema in new_name will not have an effect.
1646
+ # a rename table operation, so specifying a new schema in new_name will not have an effect.
1403
1647
  def rename_table_sql(name, new_name)
1404
1648
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
1405
1649
  end
1406
1650
 
1651
+ # Handle interval and citext types.
1407
1652
  def schema_column_type(db_type)
1408
1653
  case db_type
1409
1654
  when /\Ainterval\z/io
@@ -1415,43 +1660,48 @@ module Sequel
1415
1660
  end
1416
1661
  end
1417
1662
 
1663
+ # The schema :type entry to use for array types.
1664
+ def schema_array_type(db_type)
1665
+ :array
1666
+ end
1667
+
1668
+ # The schema :type entry to use for row/composite types.
1669
+ def schema_composite_type(db_type)
1670
+ :composite
1671
+ end
1672
+
1673
+ # The schema :type entry to use for enum types.
1674
+ def schema_enum_type(db_type)
1675
+ :enum
1676
+ end
1677
+
1678
+ # The schema :type entry to use for range types.
1679
+ def schema_range_type(db_type)
1680
+ :range
1681
+ end
1682
+
1683
+ # The schema :type entry to use for multirange types.
1684
+ def schema_multirange_type(db_type)
1685
+ :multirange
1686
+ end
1687
+
1688
+ MIN_DATE = Date.new(-4713, 11, 24)
1689
+ MAX_DATE = Date.new(5874897, 12, 31)
1690
+ MIN_TIMESTAMP = Time.utc(-4713, 11, 24).freeze
1691
+ MAX_TIMESTAMP = (Time.utc(294277) - Rational(1, 1000000)).freeze
1692
+ TYPTYPE_METHOD_MAP = {
1693
+ 'c' => :schema_composite_type,
1694
+ 'e' => :schema_enum_type,
1695
+ 'r' => :schema_range_type,
1696
+ 'm' => :schema_multirange_type,
1697
+ }
1698
+ TYPTYPE_METHOD_MAP.default = :schema_column_type
1699
+ TYPTYPE_METHOD_MAP.freeze
1418
1700
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
1419
1701
  def schema_parse_table(table_name, opts)
1420
1702
  m = output_identifier_meth(opts[:dataset])
1421
- oid = regclass_oid(table_name, opts)
1422
- ds = metadata_dataset.select{[
1423
- pg_attribute[:attname].as(:name),
1424
- SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1425
- SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1426
- SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1427
- SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1428
- SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1429
- SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1430
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
1431
- from(:pg_class).
1432
- join(:pg_attribute, :attrelid=>:oid).
1433
- join(:pg_type, :oid=>:atttypid).
1434
- left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1435
- left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1436
- left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1437
- where{{pg_attribute[:attisdropped]=>false}}.
1438
- where{pg_attribute[:attnum] > 0}.
1439
- where{{pg_class[:oid]=>oid}}.
1440
- order{pg_attribute[:attnum]}
1441
-
1442
- # :nocov:
1443
- if server_version > 100000
1444
- # :nocov:
1445
- ds = ds.select_append{pg_attribute[:attidentity]}
1446
-
1447
- # :nocov:
1448
- if server_version > 120000
1449
- # :nocov:
1450
- ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1451
- end
1452
- end
1453
1703
 
1454
- ds.map do |row|
1704
+ _schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
1455
1705
  row[:default] = nil if blank_object?(row[:default])
1456
1706
  if row[:base_oid]
1457
1707
  row[:domain_oid] = row[:oid]
@@ -1462,11 +1712,33 @@ module Sequel
1462
1712
  row.delete(:base_oid)
1463
1713
  row.delete(:db_base_type)
1464
1714
  end
1465
- row[:type] = schema_column_type(row[:db_type])
1715
+
1716
+ db_type = row[:db_type]
1717
+ row[:type] = if row.delete(:is_array)
1718
+ schema_array_type(db_type)
1719
+ else
1720
+ send(TYPTYPE_METHOD_MAP[row.delete(:typtype)], db_type)
1721
+ end
1466
1722
  identity = row.delete(:attidentity)
1467
1723
  if row[:primary_key]
1468
1724
  row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
1469
1725
  end
1726
+
1727
+ # :nocov:
1728
+ if server_version >= 90600
1729
+ # :nocov:
1730
+ case row[:oid]
1731
+ when 1082
1732
+ row[:min_value] = MIN_DATE
1733
+ row[:max_value] = MAX_DATE
1734
+ when 1184, 1114
1735
+ if Sequel.datetime_class == Time
1736
+ row[:min_value] = MIN_TIMESTAMP
1737
+ row[:max_value] = MAX_TIMESTAMP
1738
+ end
1739
+ end
1740
+ end
1741
+
1470
1742
  [m.call(row.delete(:name)), row]
1471
1743
  end
1472
1744
  end
@@ -1548,7 +1820,7 @@ module Sequel
1548
1820
 
1549
1821
  Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
1550
1822
  Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
1551
- Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
1823
+ Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values compounds order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
1552
1824
  Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
1553
1825
 
1554
1826
  # Return the results of an EXPLAIN ANALYZE query as a string
@@ -1574,8 +1846,6 @@ module Sequel
1574
1846
  literal_append(sql, args[0])
1575
1847
  sql << ' ' << op.to_s << ' '
1576
1848
  literal_append(sql, args[1])
1577
- sql << " ESCAPE "
1578
- literal_append(sql, "\\")
1579
1849
  sql << ')'
1580
1850
  else
1581
1851
  super
@@ -1600,6 +1870,12 @@ module Sequel
1600
1870
  clone(:disable_insert_returning=>true)
1601
1871
  end
1602
1872
 
1873
+ # Always return false when using VALUES
1874
+ def empty?
1875
+ return false if @opts[:values]
1876
+ super
1877
+ end
1878
+
1603
1879
  # Return the results of an EXPLAIN query as a string
1604
1880
  def explain(opts=OPTS)
1605
1881
  with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
@@ -1623,7 +1899,7 @@ module Sequel
1623
1899
  # :phrase :: Similar to :plain, but also adding an ILIKE filter to ensure that
1624
1900
  # returned rows also include the exact phrase used.
1625
1901
  # :rank :: Set to true to order by the rank, so that closer matches are returned first.
1626
- # :to_tsquery :: Can be set to :plain or :phrase to specify the function to use to
1902
+ # :to_tsquery :: Can be set to :plain, :phrase, or :websearch to specify the function to use to
1627
1903
  # convert the terms to a ts_query.
1628
1904
  # :tsquery :: Specifies the terms argument is already a valid SQL expression returning a
1629
1905
  # tsquery, and can be used directly in the query.
@@ -1643,6 +1919,8 @@ module Sequel
1643
1919
  query_func = case to_tsquery = opts[:to_tsquery]
1644
1920
  when :phrase, :plain
1645
1921
  :"#{to_tsquery}to_tsquery"
1922
+ when :websearch
1923
+ :"websearch_to_tsquery"
1646
1924
  else
1647
1925
  (opts[:phrase] || opts[:plain]) ? :plainto_tsquery : :to_tsquery
1648
1926
  end
@@ -1907,10 +2185,14 @@ module Sequel
1907
2185
  server_version >= 90500
1908
2186
  end
1909
2187
 
2188
+ # :nocov:
2189
+
1910
2190
  # PostgreSQL supports timezones in literal timestamps
1911
2191
  def supports_timestamp_timezones?
2192
+ # SEQUEL6: Remove
1912
2193
  true
1913
2194
  end
2195
+ # :nocov:
1914
2196
 
1915
2197
  # PostgreSQL 8.4+ supports WINDOW clause.
1916
2198
  def supports_window_clause?
@@ -1976,12 +2258,10 @@ module Sequel
1976
2258
  # Otherwise, return an array of hashes.
1977
2259
  def _import(columns, values, opts=OPTS)
1978
2260
  if @opts[:returning]
1979
- 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}
2261
+ # no transaction: our multi_insert_sql_strategy should guarantee
2262
+ # that there's only ever a single statement.
2263
+ sql = multi_insert_sql(columns, values)[0]
2264
+ returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
1985
2265
  elsif opts[:return] == :primary_key
1986
2266
  returning(insert_pk)._import(columns, values, opts)
1987
2267
  else
@@ -2021,12 +2301,22 @@ module Sequel
2021
2301
  "TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
2022
2302
  end
2023
2303
 
2304
+ # Use from_self for aggregate dataset using VALUES.
2305
+ def aggreate_dataset_use_from_self?
2306
+ super || @opts[:values]
2307
+ end
2308
+
2024
2309
  # Allow truncation of multiple source tables.
2025
2310
  def check_truncation_allowed!
2026
2311
  raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
2027
2312
  raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
2028
2313
  end
2029
2314
 
2315
+ # The strftime format to use when literalizing the time.
2316
+ def default_timestamp_format
2317
+ "'%Y-%m-%d %H:%M:%S.%6N%z'"
2318
+ end
2319
+
2030
2320
  # Only include the primary table in the main delete clause
2031
2321
  def delete_from_sql(sql)
2032
2322
  sql << ' FROM '
@@ -2145,6 +2435,22 @@ module Sequel
2145
2435
  end
2146
2436
  end
2147
2437
 
2438
+ # Handle Ruby integers outside PostgreSQL bigint range specially.
2439
+ def literal_integer(v)
2440
+ if v > 9223372036854775807 || v < -9223372036854775808
2441
+ literal_integer_outside_bigint_range(v)
2442
+ else
2443
+ v.to_s
2444
+ end
2445
+ end
2446
+
2447
+ # Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
2448
+ # outside PostgreSQL bigint range, so PostgreSQL doesn't treat
2449
+ # the value as numeric.
2450
+ def literal_integer_outside_bigint_range(v)
2451
+ raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
2452
+ end
2453
+
2148
2454
  # Assume that SQL standard quoting is on, per Sequel's defaults
2149
2455
  def literal_string_append(sql, v)
2150
2456
  sql << "'" << v.gsub("'", "''") << "'"
@@ -2243,7 +2549,10 @@ module Sequel
2243
2549
  # Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
2244
2550
  def select_with_sql_cte(sql, cte)
2245
2551
  super
2552
+ select_with_sql_cte_search_cycle(sql, cte)
2553
+ end
2246
2554
 
2555
+ def select_with_sql_cte_search_cycle(sql, cte)
2247
2556
  if search_opts = cte[:search]
2248
2557
  sql << if search_opts[:type] == :breadth
2249
2558
  " SEARCH BREADTH FIRST BY "