sequel 5.106.0 → 5.109.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sequel +5 -5
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/amalgalite.rb +34 -8
  5. data/lib/sequel/adapters/ibmdb.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -1
  7. data/lib/sequel/adapters/jdbc/mssql.rb +28 -5
  8. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +14 -3
  10. data/lib/sequel/adapters/jdbc.rb +4 -3
  11. data/lib/sequel/adapters/mock.rb +1 -1
  12. data/lib/sequel/adapters/mysql.rb +7 -2
  13. data/lib/sequel/adapters/mysql2.rb +5 -4
  14. data/lib/sequel/adapters/postgres.rb +42 -39
  15. data/lib/sequel/adapters/shared/db2.rb +5 -2
  16. data/lib/sequel/adapters/shared/mssql.rb +11 -5
  17. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  18. data/lib/sequel/adapters/shared/postgres.rb +80 -92
  19. data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -32
  20. data/lib/sequel/adapters/shared/sqlite.rb +8 -7
  21. data/lib/sequel/adapters/sqlite.rb +13 -9
  22. data/lib/sequel/adapters/trilogy.rb +2 -1
  23. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +11 -1
  25. data/lib/sequel/connection_pool/sharded_single.rb +1 -1
  26. data/lib/sequel/connection_pool/sharded_threaded.rb +7 -5
  27. data/lib/sequel/connection_pool/sharded_timed_queue.rb +3 -3
  28. data/lib/sequel/connection_pool/threaded.rb +9 -7
  29. data/lib/sequel/connection_pool/timed_queue.rb +2 -2
  30. data/lib/sequel/connection_pool.rb +3 -3
  31. data/lib/sequel/core.rb +44 -24
  32. data/lib/sequel/database/connecting.rb +1 -1
  33. data/lib/sequel/database/features.rb +1 -1
  34. data/lib/sequel/database/misc.rb +6 -11
  35. data/lib/sequel/database/query.rb +2 -2
  36. data/lib/sequel/database/schema_generator.rb +7 -5
  37. data/lib/sequel/database/schema_methods.rb +2 -2
  38. data/lib/sequel/database/transactions.rb +5 -7
  39. data/lib/sequel/dataset/actions.rb +2 -2
  40. data/lib/sequel/dataset/features.rb +4 -4
  41. data/lib/sequel/dataset/graph.rb +1 -1
  42. data/lib/sequel/dataset/misc.rb +3 -3
  43. data/lib/sequel/dataset/prepared_statements.rb +2 -2
  44. data/lib/sequel/dataset/query.rb +8 -8
  45. data/lib/sequel/dataset/sql.rb +32 -13
  46. data/lib/sequel/dataset.rb +2 -2
  47. data/lib/sequel/deprecated.rb +2 -2
  48. data/lib/sequel/exceptions.rb +2 -2
  49. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  50. data/lib/sequel/extensions/async_thread_pool.rb +3 -3
  51. data/lib/sequel/extensions/auto_cast_date_and_time.rb +2 -2
  52. data/lib/sequel/extensions/blank.rb +2 -2
  53. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  54. data/lib/sequel/extensions/core_refinements.rb +7 -10
  55. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  56. data/lib/sequel/extensions/datetime_parse_to_time.rb +2 -2
  57. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -3
  58. data/lib/sequel/extensions/graph_each.rb +1 -1
  59. data/lib/sequel/extensions/index_caching.rb +1 -1
  60. data/lib/sequel/extensions/inflector.rb +5 -5
  61. data/lib/sequel/extensions/is_distinct_from.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +20 -4
  63. data/lib/sequel/extensions/migration.rb +21 -7
  64. data/lib/sequel/extensions/named_timezones.rb +9 -9
  65. data/lib/sequel/extensions/pg_array.rb +10 -8
  66. data/lib/sequel/extensions/pg_array_ops.rb +69 -14
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +2 -2
  68. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +3 -3
  69. data/lib/sequel/extensions/pg_enum.rb +2 -2
  70. data/lib/sequel/extensions/pg_extended_date_support.rb +5 -5
  71. data/lib/sequel/extensions/pg_extended_integer_support.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  73. data/lib/sequel/extensions/pg_hstore_ops.rb +5 -5
  74. data/lib/sequel/extensions/pg_inet.rb +2 -2
  75. data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
  76. data/lib/sequel/extensions/pg_interval.rb +6 -6
  77. data/lib/sequel/extensions/pg_json.rb +4 -4
  78. data/lib/sequel/extensions/pg_json_ops.rb +15 -13
  79. data/lib/sequel/extensions/pg_range.rb +6 -6
  80. data/lib/sequel/extensions/pg_range_ops.rb +7 -7
  81. data/lib/sequel/extensions/pg_row.rb +4 -4
  82. data/lib/sequel/extensions/pg_row_ops.rb +6 -6
  83. data/lib/sequel/extensions/provenance.rb +2 -2
  84. data/lib/sequel/extensions/query.rb +2 -2
  85. data/lib/sequel/extensions/s.rb +6 -10
  86. data/lib/sequel/extensions/schema_dumper.rb +10 -6
  87. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +2 -0
  88. data/lib/sequel/extensions/sql_comments.rb +1 -1
  89. data/lib/sequel/extensions/sql_log_normalizer.rb +1 -1
  90. data/lib/sequel/extensions/sqlite_json_ops.rb +2 -2
  91. data/lib/sequel/extensions/string_agg.rb +5 -5
  92. data/lib/sequel/extensions/symbol_aref.rb +12 -32
  93. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -5
  94. data/lib/sequel/extensions/symbol_as.rb +1 -2
  95. data/lib/sequel/extensions/symbol_as_refinement.rb +1 -5
  96. data/lib/sequel/extensions/transaction_connection_validator.rb +1 -1
  97. data/lib/sequel/model/associations.rb +17 -17
  98. data/lib/sequel/model/base.rb +18 -15
  99. data/lib/sequel/model/inflections.rb +1 -1
  100. data/lib/sequel/model/plugins.rb +2 -2
  101. data/lib/sequel/model.rb +2 -2
  102. data/lib/sequel/plugins/association_pks.rb +1 -1
  103. data/lib/sequel/plugins/association_proxies.rb +4 -4
  104. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +2 -0
  105. data/lib/sequel/plugins/caching.rb +28 -3
  106. data/lib/sequel/plugins/class_table_inheritance.rb +1 -1
  107. data/lib/sequel/plugins/column_encryption.rb +10 -10
  108. data/lib/sequel/plugins/concurrent_eager_loading.rb +2 -2
  109. data/lib/sequel/plugins/csv_serializer.rb +9 -20
  110. data/lib/sequel/plugins/deprecated_associations.rb +2 -2
  111. data/lib/sequel/plugins/eager_each.rb +1 -1
  112. data/lib/sequel/plugins/finder.rb +1 -1
  113. data/lib/sequel/plugins/forbid_lazy_load.rb +3 -3
  114. data/lib/sequel/plugins/insert_conflict.rb +1 -1
  115. data/lib/sequel/plugins/insert_returning_select.rb +1 -1
  116. data/lib/sequel/plugins/instance_filters.rb +8 -0
  117. data/lib/sequel/plugins/instance_hooks.rb +19 -0
  118. data/lib/sequel/plugins/json_serializer.rb +4 -4
  119. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  120. data/lib/sequel/plugins/many_through_many.rb +1 -1
  121. data/lib/sequel/plugins/mssql_optimistic_locking.rb +9 -0
  122. data/lib/sequel/plugins/optimistic_locking.rb +12 -2
  123. data/lib/sequel/plugins/optimistic_locking_base.rb +14 -0
  124. data/lib/sequel/plugins/pg_array_associations.rb +4 -4
  125. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -3
  126. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +2 -2
  127. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +11 -2
  128. data/lib/sequel/plugins/prepared_statements.rb +2 -2
  129. data/lib/sequel/plugins/select_on_skipped_update.rb +63 -0
  130. data/lib/sequel/plugins/serialization.rb +1 -1
  131. data/lib/sequel/plugins/sharding.rb +1 -1
  132. data/lib/sequel/plugins/single_table_inheritance.rb +21 -3
  133. data/lib/sequel/plugins/skip_saving_columns.rb +2 -2
  134. data/lib/sequel/plugins/split_values.rb +1 -1
  135. data/lib/sequel/plugins/sql_comments.rb +8 -8
  136. data/lib/sequel/plugins/subclasses.rb +6 -6
  137. data/lib/sequel/plugins/subset_conditions.rb +5 -16
  138. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  139. data/lib/sequel/plugins/throw_failures.rb +3 -3
  140. data/lib/sequel/plugins/unused_associations.rb +5 -5
  141. data/lib/sequel/plugins/validate_associated.rb +7 -1
  142. data/lib/sequel/plugins/validate_associated_context.rb +52 -0
  143. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  144. data/lib/sequel/plugins/xml_serializer.rb +2 -0
  145. data/lib/sequel/sql.rb +120 -116
  146. data/lib/sequel/version.rb +1 -1
  147. metadata +5 -3
@@ -42,7 +42,7 @@ module Sequel
42
42
  #
43
43
  # :result :: The result code of the stored procedure
44
44
  # :numrows :: The number of rows affected by the stored procedure
45
- # output params :: Values for any output paramters, using the name given for the output parameter
45
+ # output params :: Values for any output parameters, using the name given for the output parameter
46
46
  #
47
47
  # Because Sequel datasets only support a single result set per query, and retrieving
48
48
  # the result code and number of rows requires a query, this does not support
@@ -400,6 +400,9 @@ module Sequel
400
400
  # Error if a string is given.
401
401
  def create_table_as(name, ds, options)
402
402
  raise(Error, "must provide dataset instance as value of create_table :as option on MSSQL") unless ds.is_a?(Sequel::Dataset)
403
+ if options[:temp]
404
+ name = create_table_temp_table_name_sql(name, options)
405
+ end
403
406
  run(ds.into(name).sql.freeze)
404
407
  end
405
408
 
@@ -672,14 +675,17 @@ module Sequel
672
675
  end
673
676
  end
674
677
 
675
- # MSSQL treats [] as a metacharacter in LIKE expresions.
678
+ # MSSQL treats [] as a metacharacter in LIKE expressions.
676
679
  def escape_like(string)
677
680
  string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
678
681
  end
679
682
 
680
683
  # MSSQL uses the CONTAINS keyword for full text search
681
684
  def full_text_search(cols, terms, opts = OPTS)
682
- terms = "\"#{Sequel.array_or_set_join(terms, '" OR "')}\"" if terms.is_a?(Array) || terms.is_a?(Set)
685
+ case terms
686
+ when Array, Set
687
+ terms = Sequel.array_or_set_join(terms.map{|term| "\"#{term.to_s.gsub('"', '""')}\""}, " OR ")
688
+ end
683
689
  where(Sequel.lit("CONTAINS (?, ?)", cols, terms))
684
690
  end
685
691
 
@@ -941,7 +947,7 @@ module Sequel
941
947
  # Allow update and delete for unordered, limited datasets only.
942
948
  def check_not_limited!(type)
943
949
  return if @opts[:skip_limit_check] && type != :truncate
944
- raise Sequel::InvalidOperation, "Dataset##{type} not suppored on ordered, limited datasets" if opts[:order] && opts[:limit]
950
+ raise Sequel::InvalidOperation, "Dataset##{type} not supported on ordered, limited datasets" if opts[:order] && opts[:limit]
945
951
  super if type == :truncate || @opts[:offset]
946
952
  end
947
953
 
@@ -1040,7 +1046,7 @@ module Sequel
1040
1046
  end
1041
1047
  end
1042
1048
 
1043
- # MSSQL uses a literal hexidecimal number for blob strings
1049
+ # MSSQL uses a literal hexadecimal number for blob strings
1044
1050
  def literal_blob_append(sql, v)
1045
1051
  sql << '0x' << v.unpack("H*").first
1046
1052
  end
@@ -381,7 +381,7 @@ module Sequel
381
381
  'AUTO_INCREMENT'
382
382
  end
383
383
 
384
- # MySQL needs to set transaction isolation before begining a transaction
384
+ # MySQL needs to set transaction isolation before beginning a transaction
385
385
  def begin_new_transaction(conn, opts)
386
386
  set_transaction_isolation(conn, opts)
387
387
  log_connection_execute(conn, begin_transaction_sql)
@@ -937,7 +937,7 @@ module Sequel
937
937
  end
938
938
 
939
939
  # Check the database setting for whether fractional timestamps
940
- # are suppported.
940
+ # are supported.
941
941
  def supports_timestamp_usecs?
942
942
  db.supports_timestamp_usecs?
943
943
  end
@@ -204,12 +204,12 @@ module Sequel
204
204
  @in = v
205
205
  end
206
206
 
207
- # Assumes hash partitioning, sets the modulus for this parition.
207
+ # Assumes hash partitioning, sets the modulus for this partition.
208
208
  def modulus(v)
209
209
  @modulus = v
210
210
  end
211
211
 
212
- # Assumes hash partitioning, sets the remainder for this parition.
212
+ # Assumes hash partitioning, sets the remainder for this partition.
213
213
  def remainder(v)
214
214
  @remainder = v
215
215
  end
@@ -346,7 +346,7 @@ module Sequel
346
346
  # Keys are CHECK constraint name symbols. Values are hashes with the following keys:
347
347
  # :definition :: An SQL fragment for the definition of the constraint
348
348
  # :columns :: An array of column symbols for the columns referenced in the constraint,
349
- # can be an empty array if the database cannot deteremine the column symbols.
349
+ # can be an empty array if the database cannot determine the column symbols.
350
350
  def check_constraints(table)
351
351
  m = output_identifier_meth
352
352
 
@@ -772,12 +772,12 @@ module Sequel
772
772
  seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
773
773
  increment_by = :seqincrement
774
774
  min_value = :seqmin
775
- # :nocov:
775
+ # simplecov:disable
776
776
  else
777
777
  seq_ds = metadata_dataset.from(LiteralString.new(seq))
778
778
  increment_by = :increment_by
779
779
  min_value = :min_value
780
- # :nocov:
780
+ # simplecov:enable
781
781
  end
782
782
 
783
783
  get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
@@ -790,9 +790,9 @@ module Sequel
790
790
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
791
791
  # managing incrementing primary keys.
792
792
  def serial_primary_key_options
793
- # :nocov:
793
+ # simplecov:disable
794
794
  auto_increment_key = server_version >= 100002 ? :identity : :serial
795
- # :nocov:
795
+ # simplecov:enable
796
796
  {:primary_key => true, auto_increment_key => true, :type=>Integer}
797
797
  end
798
798
 
@@ -979,12 +979,12 @@ module Sequel
979
979
  if server_version >= 90500
980
980
  cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
981
981
  rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
982
- # :nocov:
982
+ # simplecov:disable
983
983
  else
984
984
  range = 0...32
985
985
  cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
986
986
  rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
987
- # :nocov:
987
+ # simplecov:enable
988
988
  end
989
989
 
990
990
  ds = metadata_dataset.
@@ -1021,19 +1021,19 @@ module Sequel
1021
1021
  def _add_validated_enforced_constraint_columns(ds)
1022
1022
  validated_cond = if server_version >= 90100
1023
1023
  Sequel[:convalidated]
1024
- # :nocov:
1024
+ # simplecov:disable
1025
1025
  else
1026
1026
  Sequel.cast(true, TrueClass)
1027
- # :nocov:
1027
+ # simplecov:enable
1028
1028
  end
1029
1029
  ds = ds.select_append(validated_cond.as(:validated))
1030
1030
 
1031
1031
  enforced_cond = if server_version >= 180000
1032
1032
  Sequel[:conenforced]
1033
- # :nocov:
1033
+ # simplecov:disable
1034
1034
  else
1035
1035
  Sequel.cast(true, TrueClass)
1036
- # :nocov:
1036
+ # simplecov:enable
1037
1037
  end
1038
1038
  ds = ds.select_append(enforced_cond.as(:enforced))
1039
1039
 
@@ -1045,11 +1045,11 @@ module Sequel
1045
1045
  @_indexes_ds ||= begin
1046
1046
  if server_version >= 90500
1047
1047
  order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
1048
- # :nocov:
1048
+ # simplecov:disable
1049
1049
  else
1050
1050
  range = 0...32
1051
1051
  order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
1052
- # :nocov:
1052
+ # simplecov:enable
1053
1053
  end
1054
1054
 
1055
1055
  attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
@@ -1067,10 +1067,10 @@ module Sequel
1067
1067
  order(*order).
1068
1068
  select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
1069
1069
 
1070
- # :nocov:
1070
+ # simplecov:disable
1071
1071
  ds = ds.where(:indisready=>true) if server_version >= 80300
1072
1072
  ds = ds.where(:indislive=>true) if server_version >= 90300
1073
- # :nocov:
1073
+ # simplecov:enable
1074
1074
 
1075
1075
  ds
1076
1076
  end
@@ -1163,14 +1163,14 @@ module Sequel
1163
1163
  where{pg_attribute[:attnum] > 0}.
1164
1164
  order{pg_attribute[:attnum]}
1165
1165
 
1166
- # :nocov:
1166
+ # simplecov:disable
1167
1167
  if server_version > 100000
1168
- # :nocov:
1168
+ # simplecov:enable
1169
1169
  ds = ds.select_append{pg_attribute[:attidentity]}
1170
1170
 
1171
- # :nocov:
1171
+ # simplecov:disable
1172
1172
  if server_version > 120000
1173
- # :nocov:
1173
+ # simplecov:enable
1174
1174
  ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1175
1175
  end
1176
1176
  end
@@ -1261,7 +1261,7 @@ module Sequel
1261
1261
 
1262
1262
  # If the :synchronous option is given and non-nil, set synchronous_commit
1263
1263
  # appropriately. Valid values for the :synchronous option are true,
1264
- # :on, false, :off, :local, and :remote_write.
1264
+ # :on, false, :off, :local, :remote_apply, and :remote_write.
1265
1265
  def begin_new_transaction(conn, opts)
1266
1266
  super
1267
1267
  if opts.has_key?(:synchronous)
@@ -1272,6 +1272,12 @@ module Sequel
1272
1272
  sync = :off
1273
1273
  when nil
1274
1274
  return
1275
+ when :on, :off, :local, :remote_apply, :remote_write
1276
+ nil
1277
+ else
1278
+ # SEQUEL6: raise
1279
+ Sequel::Deprecation.deprecate("Unsupported :synchronous option given to Database#transaction: #{sync.inspect}, this will raise an error in Sequel 6.")
1280
+ return
1275
1281
  end
1276
1282
 
1277
1283
  log_connection_execute(conn, "SET LOCAL synchronous_commit = #{sync}")
@@ -1526,8 +1532,14 @@ module Sequel
1526
1532
  if opts[:options] || opts[:format]
1527
1533
  options = String.new
1528
1534
  options << " ("
1529
- options << "FORMAT #{opts[:format]}" if opts[:format]
1530
- options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
1535
+ if format = opts[:format]
1536
+ sql_format = format.to_s.downcase
1537
+ unless %w[text csv json binary].freeze.include?(sql_format)
1538
+ raise Error, "unsupported COPY TO format: #{format.inspect}"
1539
+ end
1540
+ options << "FORMAT #{format}"
1541
+ end
1542
+ options << "#{', ' if format}#{opts[:options]}" if opts[:options]
1531
1543
  options << ')'
1532
1544
  end
1533
1545
  table = if table.is_a?(::Sequel::Dataset)
@@ -1623,7 +1635,7 @@ module Sequel
1623
1635
  "#{super}#{create_table_suffix_sql(name, options)}"
1624
1636
  end
1625
1637
 
1626
- # Handle various PostgreSQl specific table extensions such as inheritance,
1638
+ # Handle various PostgreSQL specific table extensions such as inheritance,
1627
1639
  # partitioning, tablespaces, and foreign tables.
1628
1640
  def create_table_suffix_sql(name, options)
1629
1641
  sql = String.new
@@ -1922,9 +1934,9 @@ module Sequel
1922
1934
  row[:auto_increment] = !!(row[:default] =~ /\A(?:nextval)/i) || identity == 'a' || identity == 'd'
1923
1935
  end
1924
1936
 
1925
- # :nocov:
1937
+ # simplecov:disable
1926
1938
  if server_version >= 90600
1927
- # :nocov:
1939
+ # simplecov:enable
1928
1940
  case row[:oid]
1929
1941
  when 1082
1930
1942
  row[:min_value] = MIN_DATE
@@ -2013,9 +2025,9 @@ module Sequel
2013
2025
 
2014
2026
  # PostgreSQL 9.4+ supports views with check option.
2015
2027
  def view_with_check_option_support
2016
- # :nocov:
2028
+ # simplecov:disable
2017
2029
  :local if server_version >= 90400
2018
- # :nocov:
2030
+ # simplecov:enable
2019
2031
  end
2020
2032
  end
2021
2033
 
@@ -2118,12 +2130,12 @@ module Sequel
2118
2130
  rows[0]
2119
2131
  elsif rows.all?{|row| String === row}
2120
2132
  rows.join("\r\n")
2121
- # :nocov:
2133
+ # simplecov:disable
2122
2134
  else
2123
2135
  # This branch is unreachable in tests, but it seems better to just return
2124
2136
  # all rows than throw in error if this case actually happens.
2125
2137
  rows
2126
- # :nocov:
2138
+ # simplecov:enable
2127
2139
  end
2128
2140
  end
2129
2141
 
@@ -2133,26 +2145,6 @@ module Sequel
2133
2145
  cached_lock_style_dataset(:_for_key_share_ds, :key_share)
2134
2146
  end
2135
2147
 
2136
- # Set FOR PORTION OF clause for UPDATE and DELETE statements.
2137
- # The first argument is the range or multirange column. If two arguments
2138
- # are provided, the second argument is an expression with the same
2139
- # database type as the first argument. If three arguments are provided,
2140
- # the second specifies the inclusive start of the portion to update and the third
2141
- # specifies the exclusive end of portion to update. When using the three argument
2142
- # form, nil can be provided as the second or third argument to have the start or
2143
- # end of the portion be unbounded. Supported on PostgreSQL 19+.
2144
- # Example:
2145
- #
2146
- # DB[:t].for_portion_of(:rc, Sequel.function(:int4range, 1, 2)).update(c: 3)
2147
- # # UPDATE t FOR PORTION OF rc (int4range(1, 2)) SET c = 3
2148
- #
2149
- # DB[:t].for_portion_of(:rc, 1, 2).update(c: 3)
2150
- # # UPDATE t FOR PORTION OF rc FROM 1 TO 2 SET c = 3
2151
- def for_portion_of(column, range, to=(arg_not_given=true))
2152
- range = [range, to].freeze unless arg_not_given
2153
- clone(:for_portion_of => [column, range].freeze)
2154
- end
2155
-
2156
2148
  # Return a cloned dataset which will use FOR NO KEY UPDATE to lock returned rows.
2157
2149
  # This is generally a better choice than using for_update on PostgreSQL, unless
2158
2150
  # you will be deleting the row or modifying a key column. Supported on PostgreSQL 9.3+.
@@ -2245,8 +2237,11 @@ module Sequel
2245
2237
  # Handle uniqueness violations when inserting, by updating the conflicting row, using
2246
2238
  # ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
2247
2239
  # :conflict_where :: The index filter, when using a partial index to determine uniqueness.
2248
- # :constraint :: An explicit constraint name, has precendence over :target.
2240
+ # :constraint :: An explicit constraint name, has precedence over :target.
2249
2241
  # :target :: The column name or expression to handle uniqueness violations on.
2242
+ # :select :: Use ON CONFLICT DO SELECT. This will use the dataset's lock style.
2243
+ # You must use #returning on this dataset when using this option.
2244
+ # :select_where :: A WHERE condition to use for the select.
2250
2245
  # :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
2251
2246
  # :update_where :: A WHERE condition to use for the update.
2252
2247
  #
@@ -2277,7 +2272,21 @@ module Sequel
2277
2272
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
2278
2273
  # # ON CONFLICT ON CONSTRAINT table_a_uidx
2279
2274
  # # DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
2275
+ #
2276
+ # DB[:table].returning(:a).insert_conflict(target: :a, select: true).insert(a: 1, b: 2)
2277
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
2278
+ # # ON CONFLICT (a) DO SELECT RETURNING a
2279
+ #
2280
+ # DB[:table].returning(:a).for_update.
2281
+ # insert_conflict(target: :a, select: true, select_where: {Sequel[:excluded][:b] => 3}).
2282
+ # insert(a: 1, b: 2)
2283
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
2284
+ # # ON CONFLICT (a) DO SELECT FOR UPDATE WHERE (excluded.b = 3) RETURNING a
2280
2285
  def insert_conflict(opts=OPTS)
2286
+ if opts[:select] && opts[:update]
2287
+ raise Error, "Cannot provide both :select and :update options to insert_conflict"
2288
+ end
2289
+
2281
2290
  clone(:insert_conflict => opts)
2282
2291
  end
2283
2292
 
@@ -2518,14 +2527,14 @@ module Sequel
2518
2527
  server_version >= 90500
2519
2528
  end
2520
2529
 
2521
- # :nocov:
2530
+ # simplecov:disable
2522
2531
 
2523
2532
  # PostgreSQL supports timezones in literal timestamps
2524
2533
  def supports_timestamp_timezones?
2525
2534
  # SEQUEL6: Remove
2526
2535
  true
2527
2536
  end
2528
- # :nocov:
2537
+ # simplecov:enable
2529
2538
 
2530
2539
  # PostgreSQL 8.4+ supports WINDOW clause.
2531
2540
  def supports_window_clause?
@@ -2640,7 +2649,7 @@ module Sequel
2640
2649
  end
2641
2650
 
2642
2651
  # Use from_self for aggregate dataset using VALUES.
2643
- def aggreate_dataset_use_from_self?
2652
+ def aggregate_dataset_use_from_self?
2644
2653
  super || @opts[:values]
2645
2654
  end
2646
2655
 
@@ -2655,11 +2664,10 @@ module Sequel
2655
2664
  "'%Y-%m-%d %H:%M:%S.%6N%z'"
2656
2665
  end
2657
2666
 
2658
- # Only include the primary table in the main delete clause.
2659
- # Support FOR PORTION OF.
2667
+ # Only include the primary table in the main delete clause
2660
2668
  def delete_from_sql(sql)
2661
2669
  sql << ' FROM '
2662
- table_for_portion_of_sql_append(sql)
2670
+ source_list_append(sql, @opts[:from][0..0])
2663
2671
  end
2664
2672
 
2665
2673
  # Use USING to specify additional tables in a delete query
@@ -2702,7 +2710,7 @@ module Sequel
2702
2710
  origin = String.new
2703
2711
  origin << 'EXPLAIN '
2704
2712
 
2705
- # :nocov:
2713
+ # simplecov:disable
2706
2714
  if server_version < 90000
2707
2715
  if opts[:analyze]
2708
2716
  origin << 'ANALYZE '
@@ -2710,7 +2718,7 @@ module Sequel
2710
2718
 
2711
2719
  return origin
2712
2720
  end
2713
- # :nocov:
2721
+ # simplecov:enable
2714
2722
 
2715
2723
  comma = nil
2716
2724
  paren = "("
@@ -2744,32 +2752,6 @@ module Sequel
2744
2752
  origin
2745
2753
  end
2746
2754
 
2747
- # Add FOR PORTION OF SQL if the dataset uses it.
2748
- def table_for_portion_of_sql_append(sql)
2749
- fpo_column, fpo_range = @opts[:for_portion_of]
2750
- if fpo_column
2751
- table, aliaz = split_alias(@opts[:from].first)
2752
- source_list_append(sql, [table])
2753
- sql << ' FOR PORTION OF '
2754
- literal_append(sql, fpo_column)
2755
-
2756
- if fpo_range.is_a?(Array)
2757
- fpo_start, fpo_end = fpo_range
2758
- sql << ' FROM '
2759
- literal_append(sql, fpo_start)
2760
- sql << ' TO '
2761
- literal_append(sql, fpo_end)
2762
- else
2763
- sql << ' ('
2764
- literal_append(sql, fpo_range)
2765
- sql << ')'
2766
- end
2767
- as_sql_append(sql, aliaz) if aliaz
2768
- else
2769
- source_list_append(sql, @opts[:from][0..0])
2770
- end
2771
- end
2772
-
2773
2755
  # Add ON CONFLICT clause if it should be used
2774
2756
  def insert_conflict_sql(sql)
2775
2757
  if opts = @opts[:insert_conflict]
@@ -2790,13 +2772,19 @@ module Sequel
2790
2772
  if values = opts[:update]
2791
2773
  sql << " DO UPDATE SET "
2792
2774
  update_sql_values_hash(sql, values)
2793
- if update_where = opts[:update_where]
2794
- sql << " WHERE "
2795
- literal_append(sql, update_where)
2796
- end
2775
+ where = opts[:update_where]
2776
+ elsif opts[:select]
2777
+ sql << " DO SELECT"
2778
+ select_lock_sql(sql)
2779
+ where = opts[:select_where]
2797
2780
  else
2798
2781
  sql << " DO NOTHING"
2799
2782
  end
2783
+
2784
+ if where
2785
+ sql << " WHERE "
2786
+ literal_append(sql, where)
2787
+ end
2800
2788
  end
2801
2789
  end
2802
2790
 
@@ -3059,10 +3047,10 @@ module Sequel
3059
3047
  join_from_sql(:FROM, sql)
3060
3048
  end
3061
3049
 
3062
- # Support FOR PORTION OF.
3050
+ # Only include the primary table in the main update clause
3063
3051
  def update_table_sql(sql)
3064
3052
  sql << ' '
3065
- table_for_portion_of_sql_append(sql)
3053
+ source_list_append(sql, @opts[:from][0..0])
3066
3054
  end
3067
3055
  end
3068
3056
  end
@@ -305,38 +305,6 @@ module Sequel
305
305
  super(sql, :+, args)
306
306
  when :<<, :>>
307
307
  complex_expression_emulate_append(sql, op, args)
308
- when :LIKE, :"NOT LIKE"
309
- sql << '('
310
- literal_append(sql, args[0])
311
- sql << (op == :LIKE ? ' REGEXP ' : ' NOT REGEXP ')
312
- pattern = String.new
313
- last_c = ''
314
- args[1].each_char do |c|
315
- if c == '_' and not pattern.end_with?('\\') and last_c != '\\'
316
- pattern << '.'
317
- elsif c == '%' and not pattern.end_with?('\\') and last_c != '\\'
318
- pattern << '.*'
319
- elsif c == '[' and not pattern.end_with?('\\') and last_c != '\\'
320
- pattern << '\['
321
- elsif c == ']' and not pattern.end_with?('\\') and last_c != '\\'
322
- pattern << '\]'
323
- elsif c == '*' and not pattern.end_with?('\\') and last_c != '\\'
324
- pattern << '\*'
325
- elsif c == '?' and not pattern.end_with?('\\') and last_c != '\\'
326
- pattern << '\?'
327
- else
328
- pattern << c
329
- end
330
- if c == '\\' and last_c == '\\'
331
- last_c = ''
332
- else
333
- last_c = c
334
- end
335
- end
336
- literal_append(sql, pattern)
337
- sql << " ESCAPE "
338
- literal_append(sql, "\\")
339
- sql << ')'
340
308
  when :ILIKE, :"NOT ILIKE"
341
309
  super(sql, (op == :ILIKE ? :LIKE : :"NOT LIKE"), args)
342
310
  when :extract
@@ -210,7 +210,7 @@ module Sequel
210
210
  if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint || op[:op] == :set_column_null}
211
211
  null_ops, ops = ops.partition{|op| op[:op] == :set_column_null}
212
212
 
213
- # Apply NULL/NOT NULL ops first, since those should be purely idependent of the constraints.
213
+ # Apply NULL/NOT NULL ops first, since those should be purely independent of the constraints.
214
214
  null_ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
215
215
 
216
216
  # If you are just doing constraints, apply all of them at the same time,
@@ -677,14 +677,15 @@ module Sequel
677
677
  super
678
678
  end
679
679
 
680
- # Return an array of strings specifying a query explanation for a SELECT of the
681
- # current dataset. Currently, the options are ignored, but it accepts options
682
- # to be compatible with other adapters.
680
+ # Return a string specifying a query explanation for a SELECT of the
681
+ # current dataset. Options:
682
+ # :query_plan :: Use EXPLAIN QUERY PLAN instead of EXPLAIN if true.
683
683
  def explain(opts=nil)
684
684
  # Load the PrettyTable class, needed for explain output
685
685
  Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
686
686
 
687
- ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
687
+ keyword = (opts && opts[:query_plan]) ? "EXPLAIN QUERY PLAN" : "EXPLAIN"
688
+ ds = db.send(:metadata_dataset).clone(:sql=>"#{keyword} #{select_sql}".freeze)
688
689
  rows = ds.all
689
690
  Sequel::PrettyTable.string(rows, ds.columns)
690
691
  end
@@ -729,7 +730,7 @@ module Sequel
729
730
 
730
731
  # Handle uniqueness violations when inserting, by using a specified
731
732
  # resolution algorithm. With no options, uses INSERT OR REPLACE. SQLite
732
- # supports the following conflict resolution algoriths: ROLLBACK, ABORT,
733
+ # supports the following conflict resolution algorithms: ROLLBACK, ABORT,
733
734
  # FAIL, IGNORE and REPLACE.
734
735
  #
735
736
  # On SQLite 3.24.0+, you can pass a hash to use an ON CONFLICT clause.
@@ -891,7 +892,7 @@ module Sequel
891
892
  end
892
893
 
893
894
  # Use from_self for aggregate dataset using VALUES.
894
- def aggreate_dataset_use_from_self?
895
+ def aggregate_dataset_use_from_self?
895
896
  super || @opts[:values]
896
897
  end
897
898
 
@@ -336,16 +336,20 @@ module Sequel
336
336
  log_sql << sql
337
337
  log_sql << ")"
338
338
  end
339
- if block
340
- log_connection_yield(log_sql, conn, args){cps.execute(ps_args, &block)}
341
- else
342
- log_connection_yield(log_sql, conn, args){cps.execute!(ps_args){|r|}}
343
- case type
344
- when :insert
345
- conn.last_insert_row_id
346
- when :update
347
- conn.changes
339
+ begin
340
+ if block
341
+ log_connection_yield(log_sql, conn, args){cps.execute(ps_args, &block)}
342
+ else
343
+ log_connection_yield(log_sql, conn, args){cps.execute!(ps_args){|r|}}
344
+ case type
345
+ when :insert
346
+ conn.last_insert_row_id
347
+ when :update
348
+ conn.changes
349
+ end
348
350
  end
351
+ ensure
352
+ cps.clear_bindings!
349
353
  end
350
354
  end
351
355
 
@@ -62,7 +62,8 @@ module Sequel
62
62
  private
63
63
 
64
64
  def database_specific_error_class(exception, opts)
65
- if exception.error_code == 1205
65
+ case exception.error_code
66
+ when 1205, 3572
66
67
  DatabaseLockTimeout
67
68
  else
68
69
  super
@@ -49,7 +49,7 @@ module Sequel
49
49
  else
50
50
  # Sequel doesn't allow a nonpositive limit. If the offset
51
51
  # is greater than the number of rows, the empty result set
52
- # shuld be returned, so use a condition that is always false.
52
+ # should be returned, so use a condition that is always false.
53
53
  ds.where(1=>0)
54
54
  end
55
55
  sql = @opts[:append_sql] || String.new
@@ -22,7 +22,17 @@ module Sequel
22
22
  END
23
23
  # Error messages for mysql and mysql2 that indicate the current connection should be disconnected
24
24
  MYSQL_DATABASE_DISCONNECT_ERRORS = /\A#{Regexp.union(disconnect_errors)}/
25
-
25
+
26
+ # 2027 (CR_MALFORMED_PACKET) deliberately not included (protocol error, not connection error)
27
+ MYSQL_DISCONNECT_ERROR_CODES = [
28
+ 2002, # CR_CONNECTION_ERROR
29
+ 2006, # CR_SERVER_GONE_ERROR
30
+ 2013, # CR_SERVER_LOST
31
+ 2014, # CR_COMMANDS_OUT_OF_SYNC
32
+ 2055, # CR_SERVER_LOST_EXTENDED
33
+ ].freeze
34
+ private_constant :MYSQL_DISCONNECT_ERROR_CODES
35
+
26
36
  # Support stored procedures on MySQL
27
37
  def call_sproc(name, opts=OPTS, &block)
28
38
  args = opts[:args] || []
@@ -38,7 +38,7 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
38
38
 
39
39
  # Disconnects from the database. Once a connection is requested using
40
40
  # #hold, the connection is reestablished. Options:
41
- # :server :: Should be a symbol specifing the server to disconnect from,
41
+ # :server :: Should be a symbol specifying the server to disconnect from,
42
42
  # or an array of symbols to specify multiple servers.
43
43
  def disconnect(opts=OPTS)
44
44
  (opts[:server] ? Array(opts[:server]) : servers).each do |s|