sequel 5.48.0 → 5.52.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +80 -0
  3. data/README.rdoc +12 -5
  4. data/doc/migration.rdoc +1 -1
  5. data/doc/opening_databases.rdoc +1 -1
  6. data/doc/postgresql.rdoc +8 -0
  7. data/doc/release_notes/5.49.0.txt +59 -0
  8. data/doc/release_notes/5.50.0.txt +78 -0
  9. data/doc/release_notes/5.51.0.txt +47 -0
  10. data/doc/release_notes/5.52.0.txt +87 -0
  11. data/doc/testing.rdoc +3 -1
  12. data/lib/sequel/adapters/ado/access.rb +1 -1
  13. data/lib/sequel/adapters/ado.rb +1 -1
  14. data/lib/sequel/adapters/amalgalite.rb +3 -5
  15. data/lib/sequel/adapters/ibmdb.rb +2 -2
  16. data/lib/sequel/adapters/jdbc/derby.rb +3 -0
  17. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  18. data/lib/sequel/adapters/jdbc.rb +9 -11
  19. data/lib/sequel/adapters/mysql.rb +80 -67
  20. data/lib/sequel/adapters/mysql2.rb +42 -44
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/postgres.rb +27 -29
  24. data/lib/sequel/adapters/shared/access.rb +2 -0
  25. data/lib/sequel/adapters/shared/db2.rb +2 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +4 -2
  27. data/lib/sequel/adapters/shared/postgres.rb +59 -6
  28. data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
  29. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  30. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  31. data/lib/sequel/adapters/sqlite.rb +16 -18
  32. data/lib/sequel/adapters/tinytds.rb +1 -1
  33. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  34. data/lib/sequel/ast_transformer.rb +6 -0
  35. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  36. data/lib/sequel/connection_pool/single.rb +6 -8
  37. data/lib/sequel/core.rb +17 -18
  38. data/lib/sequel/database/connecting.rb +2 -2
  39. data/lib/sequel/database/misc.rb +6 -0
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/dataset/actions.rb +2 -2
  42. data/lib/sequel/dataset/query.rb +45 -3
  43. data/lib/sequel/dataset/sql.rb +18 -9
  44. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  45. data/lib/sequel/extensions/core_refinements.rb +36 -11
  46. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  47. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  48. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  49. data/lib/sequel/extensions/inflector.rb +1 -1
  50. data/lib/sequel/extensions/migration.rb +4 -1
  51. data/lib/sequel/extensions/pagination.rb +1 -1
  52. data/lib/sequel/extensions/pg_array_ops.rb +1 -1
  53. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  54. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  55. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  56. data/lib/sequel/extensions/pg_interval.rb +1 -0
  57. data/lib/sequel/extensions/pg_json.rb +3 -5
  58. data/lib/sequel/extensions/pg_json_ops.rb +71 -1
  59. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  60. data/lib/sequel/extensions/pg_range.rb +4 -12
  61. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  62. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  63. data/lib/sequel/extensions/s.rb +2 -1
  64. data/lib/sequel/extensions/server_block.rb +8 -12
  65. data/lib/sequel/extensions/sql_comments.rb +108 -3
  66. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  67. data/lib/sequel/extensions/string_agg.rb +1 -1
  68. data/lib/sequel/extensions/string_date_time.rb +19 -23
  69. data/lib/sequel/model/associations.rb +3 -1
  70. data/lib/sequel/model/base.rb +9 -13
  71. data/lib/sequel/model/inflections.rb +1 -1
  72. data/lib/sequel/plugins/auto_validations.rb +25 -5
  73. data/lib/sequel/plugins/column_encryption.rb +1 -1
  74. data/lib/sequel/plugins/composition.rb +1 -0
  75. data/lib/sequel/plugins/json_serializer.rb +2 -2
  76. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  77. data/lib/sequel/plugins/serialization.rb +1 -0
  78. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  79. data/lib/sequel/plugins/sql_comments.rb +189 -0
  80. data/lib/sequel/plugins/static_cache.rb +1 -1
  81. data/lib/sequel/plugins/subclasses.rb +28 -11
  82. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
  83. data/lib/sequel/plugins/unused_associations.rb +2 -2
  84. data/lib/sequel/plugins/update_or_create.rb +1 -1
  85. data/lib/sequel/plugins/validation_helpers.rb +7 -1
  86. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  87. data/lib/sequel/sql.rb +1 -1
  88. data/lib/sequel/timezones.rb +12 -14
  89. data/lib/sequel/version.rb +1 -1
  90. metadata +17 -4
@@ -189,26 +189,24 @@ module Sequel
189
189
  # Yield an available connection. Rescue
190
190
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
191
191
  def _execute(type, sql, opts, &block)
192
- begin
193
- synchronize(opts[:server]) do |conn|
194
- return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
195
- log_args = opts[:arguments]
196
- args = {}
197
- opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
198
- case type
199
- when :select
200
- log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
201
- when :insert
202
- log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
203
- conn.last_insert_row_id
204
- when :update
205
- log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
206
- conn.changes
207
- end
192
+ synchronize(opts[:server]) do |conn|
193
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
194
+ log_args = opts[:arguments]
195
+ args = {}
196
+ opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
197
+ case type
198
+ when :select
199
+ log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
200
+ when :insert
201
+ log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
202
+ conn.last_insert_row_id
203
+ when :update
204
+ log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
205
+ conn.changes
208
206
  end
209
- rescue SQLite3::Exception => e
210
- raise_error(e)
211
207
  end
208
+ rescue SQLite3::Exception => e
209
+ raise_error(e)
212
210
  end
213
211
 
214
212
  # The SQLite adapter does not need the pool to convert exceptions.
@@ -75,7 +75,7 @@ module Sequel
75
75
  return r.public_send(m) if m
76
76
  end
77
77
  end
78
- yield(r) if block_given?
78
+ yield(r) if defined?(yield)
79
79
  rescue TinyTds::Error => e
80
80
  raise_error(e, :disconnect=>!c.active?)
81
81
  ensure
@@ -0,0 +1,22 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ class Dataset
5
+ module ColumnsLimit1
6
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
7
+
8
+ # Use a limit of 1 instead of a limit of 0 when
9
+ # getting the columns.
10
+ def columns!
11
+ ds = clone(COLUMNS_CLONE_OPTIONS)
12
+ ds.each{break}
13
+
14
+ if cols = ds.cache[:_columns]
15
+ self.columns = cols
16
+ else
17
+ []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -80,6 +80,12 @@ module Sequel
80
80
  SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
81
81
  when SQL::Wrapper
82
82
  SQL::Wrapper.new(v(o.value))
83
+ when SQL::Expression
84
+ if o.respond_to?(:sequel_ast_transform)
85
+ o.sequel_ast_transform(method(:v))
86
+ else
87
+ o
88
+ end
83
89
  else
84
90
  o
85
91
  end
@@ -55,13 +55,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
55
55
  # Yields the connection to the supplied block for the given server.
56
56
  # This method simulates the ConnectionPool#hold API.
57
57
  def hold(server=:default)
58
- begin
59
- server = pick_server(server)
60
- yield(@conns[server] ||= make_new(server))
61
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
62
- disconnect_server(server) if disconnect_error?(e)
63
- raise
64
- end
58
+ server = pick_server(server)
59
+ yield(@conns[server] ||= make_new(server))
60
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
61
+ disconnect_server(server) if disconnect_error?(e)
62
+ raise
65
63
  end
66
64
 
67
65
  # The ShardedSingleConnectionPool always has a maximum size of 1.
@@ -24,15 +24,13 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
24
24
 
25
25
  # Yield the connection to the block.
26
26
  def hold(server=nil)
27
- begin
28
- unless c = @conn.first
29
- @conn.replace([c = make_new(:default)])
30
- end
31
- yield c
32
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
33
- disconnect if disconnect_error?(e)
34
- raise
27
+ unless c = @conn.first
28
+ @conn.replace([c = make_new(:default)])
35
29
  end
30
+ yield c
31
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
32
+ disconnect if disconnect_error?(e)
33
+ raise
36
34
  end
37
35
 
38
36
  # The SingleConnectionPool always has a maximum size of 1.
data/lib/sequel/core.rb CHANGED
@@ -278,11 +278,9 @@ module Sequel
278
278
  #
279
279
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
280
280
  def string_to_date(string)
281
- begin
282
- Date.parse(string, Sequel.convert_two_digit_years)
283
- rescue => e
284
- raise convert_exception_class(e, InvalidValue)
285
- end
281
+ Date.parse(string, Sequel.convert_two_digit_years)
282
+ rescue => e
283
+ raise convert_exception_class(e, InvalidValue)
286
284
  end
287
285
 
288
286
  # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
@@ -290,15 +288,13 @@ module Sequel
290
288
  #
291
289
  # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
292
290
  def string_to_datetime(string)
293
- begin
294
- if datetime_class == DateTime
295
- DateTime.parse(string, convert_two_digit_years)
296
- else
297
- datetime_class.parse(string)
298
- end
299
- rescue => e
300
- raise convert_exception_class(e, InvalidValue)
291
+ if datetime_class == DateTime
292
+ DateTime.parse(string, convert_two_digit_years)
293
+ else
294
+ datetime_class.parse(string)
301
295
  end
296
+ rescue => e
297
+ raise convert_exception_class(e, InvalidValue)
302
298
  end
303
299
 
304
300
  # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
@@ -306,11 +302,9 @@ module Sequel
306
302
  # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
307
303
  # DB.literal(v) # => '10:20:30'
308
304
  def string_to_time(string)
309
- begin
310
- SQLTime.parse(string)
311
- rescue => e
312
- raise convert_exception_class(e, InvalidValue)
313
- end
305
+ SQLTime.parse(string)
306
+ rescue => e
307
+ raise convert_exception_class(e, InvalidValue)
314
308
  end
315
309
 
316
310
  # Unless in single threaded mode, protects access to any mutable
@@ -400,6 +394,11 @@ module Sequel
400
394
 
401
395
  private
402
396
 
397
+ # Return a hash of date information parsed from the given string.
398
+ def _date_parse(string)
399
+ Date._parse(string)
400
+ end
401
+
403
402
  # Helper method that the database adapter class methods that are added to Sequel via
404
403
  # metaprogramming use to parse arguments.
405
404
  def adapter_method(adapter, *args, &block)
@@ -55,11 +55,11 @@ module Sequel
55
55
 
56
56
  begin
57
57
  db = c.new(opts)
58
- if block_given?
58
+ if defined?(yield)
59
59
  return yield(db)
60
60
  end
61
61
  ensure
62
- if block_given?
62
+ if defined?(yield)
63
63
  db.disconnect if db
64
64
  Sequel.synchronize{::Sequel::DATABASES.delete(db)}
65
65
  end
@@ -95,6 +95,8 @@ module Sequel
95
95
  # options hash.
96
96
  #
97
97
  # Accepts the following options:
98
+ # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
99
+ # but before any connections are created.
98
100
  # :cache_schema :: Whether schema should be cached for this Database instance
99
101
  # :default_string_column_size :: The default size of string columns, 255 by default.
100
102
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
@@ -160,6 +162,10 @@ module Sequel
160
162
 
161
163
  initialize_load_extensions(:preconnect_extensions)
162
164
 
165
+ if before_preconnect = @opts[:before_preconnect]
166
+ before_preconnect.call(self)
167
+ end
168
+
163
169
  if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
164
170
  concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
165
171
  @pool.send(:preconnect, concurrent)
@@ -236,7 +236,7 @@ module Sequel
236
236
  when :date
237
237
  Sequel.string_to_date(default)
238
238
  when :datetime
239
- DateTime.parse(default)
239
+ Sequel.string_to_datetime(default)
240
240
  when :time
241
241
  Sequel.string_to_time(default)
242
242
  when :decimal
@@ -19,7 +19,7 @@ module Sequel
19
19
  METHS
20
20
 
21
21
  # The clone options to use when retrieving columns for a dataset.
22
- COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
22
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 0, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
23
23
 
24
24
  # Inserts the given argument into the database. Returns self so it
25
25
  # can be used safely when chaining:
@@ -546,7 +546,7 @@ module Sequel
546
546
  unless @opts[:order]
547
547
  raise Sequel::Error, "Dataset#paged_each requires the dataset be ordered"
548
548
  end
549
- unless block_given?
549
+ unless defined?(yield)
550
550
  return enum_for(:paged_each, opts)
551
551
  end
552
552
 
@@ -508,6 +508,7 @@ module Sequel
508
508
  # argument.
509
509
  # :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
510
510
  # the last joined or primary table is used.
511
+ # :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
511
512
  # :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
512
513
  # for implicit conditions.
513
514
  # :qualify :: Can be set to false to not do any implicit qualification. Can be set
@@ -541,7 +542,7 @@ module Sequel
541
542
  return s.join_table(type, ds, expr, options, &block)
542
543
  end
543
544
 
544
- using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
545
+ using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
545
546
  if using_join && !supports_join_using?
546
547
  h = {}
547
548
  expr.each{|e| h[e] = e}
@@ -616,7 +617,7 @@ module Sequel
616
617
  UNCONDITIONED_JOIN_TYPES.each do |jtype|
617
618
  class_eval(<<-END, __FILE__, __LINE__+1)
618
619
  def #{jtype}_join(table, opts=Sequel::OPTS)
619
- raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?
620
+ raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
620
621
  raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
621
622
  join_table(:#{jtype}, table, nil, opts)
622
623
  end
@@ -1059,6 +1060,7 @@ module Sequel
1059
1060
 
1060
1061
  # Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
1061
1062
  # A common table expression acts as an inline view for the query.
1063
+ #
1062
1064
  # Options:
1063
1065
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1064
1066
  # :recursive :: Specify that this is a recursive CTE
@@ -1079,10 +1081,35 @@ module Sequel
1079
1081
 
1080
1082
  # Add a recursive common table expression (CTE) with the given name, a dataset that
1081
1083
  # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
1082
- # of the CTE. Options:
1084
+ # of the CTE.
1085
+ #
1086
+ # Options:
1083
1087
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1084
1088
  # :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
1085
1089
  #
1090
+ # PostgreSQL 14+ Options:
1091
+ # :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
1092
+ # result of the CTE, a cycle column indicating whether a cycle was detected for
1093
+ # the current row, and a path column for the path traversed to get to the current
1094
+ # row. If given, must be a hash with the following keys:
1095
+ # :columns :: (required) The column or array of columns to use to detect a cycle.
1096
+ # If the value of these columns match columns already traversed, then
1097
+ # a cycle is detected, and recursive searching will not traverse beyond
1098
+ # the cycle (the CTE will include the row where the cycle was detected).
1099
+ # :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
1100
+ # :cycle_value :: The value of the cycle column in the output if the current row was
1101
+ # detected as a cycle, defaults to true.
1102
+ # :noncycle_value :: The value of the cycle column in the output if the current row
1103
+ # was not detected as a cycle, defaults to false. Only respected
1104
+ # if :cycle_value is given.
1105
+ # :path_column :: The name of the path column in the output, defaults to :path.
1106
+ # :search :: Include an order column in the result of the CTE that allows for breadth or
1107
+ # depth first searching. If given, must be a hash with the following keys:
1108
+ # :by :: (required) The column or array of columns to search by.
1109
+ # :order_column :: The name of the order column in the output, defaults to :ordercol.
1110
+ # :type :: Set to :breadth to use breadth-first searching (depth-first searching
1111
+ # is the default).
1112
+ #
1086
1113
  # DB[:t].with_recursive(:t,
1087
1114
  # DB[:i1].select(:id, :parent_id).where(parent_id: nil),
1088
1115
  # DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
@@ -1093,6 +1120,21 @@ module Sequel
1093
1120
  # # UNION ALL
1094
1121
  # # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
1095
1122
  # # ) SELECT * FROM t
1123
+ #
1124
+ # DB[:t].with_recursive(:t,
1125
+ # DB[:i1].where(parent_id: nil),
1126
+ # DB[:i1].join(:t, id: :parent_id).select_all(:i1),
1127
+ # search: {by: :id, type: :breadth},
1128
+ # cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
1129
+ #
1130
+ # # WITH RECURSIVE t AS (
1131
+ # # SELECT * FROM i1 WHERE (parent_id IS NULL)
1132
+ # # UNION ALL
1133
+ # # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
1134
+ # # )
1135
+ # # SEARCH BREADTH FIRST BY id SET ordercol
1136
+ # # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
1137
+ # # SELECT * FROM t
1096
1138
  def with_recursive(name, nonrecursive, recursive, opts=OPTS)
1097
1139
  raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
1098
1140
  if hoist_cte?(nonrecursive)
@@ -559,11 +559,9 @@ module Sequel
559
559
  # Append literalization of JOIN USING clause to SQL string.
560
560
  def join_using_clause_sql_append(sql, jc)
561
561
  join_clause_sql_append(sql, jc)
562
- sql << ' USING ('
563
- column_list_append(sql, jc.using)
564
- sql << ')'
562
+ join_using_clause_using_sql_append(sql, jc.using)
565
563
  end
566
-
564
+
567
565
  # Append literalization of negative boolean constant to SQL string.
568
566
  def negative_boolean_constant_sql_append(sql, constant)
569
567
  sql << 'NOT '
@@ -1218,6 +1216,13 @@ module Sequel
1218
1216
  "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
1219
1217
  end
1220
1218
 
1219
+ # Append USING clause for JOIN USING
1220
+ def join_using_clause_using_sql_append(sql, using_columns)
1221
+ sql << ' USING ('
1222
+ column_list_append(sql, using_columns)
1223
+ sql << ')'
1224
+ end
1225
+
1221
1226
  # Append a literalization of the array to SQL string.
1222
1227
  # Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
1223
1228
  def literal_array_append(sql, v)
@@ -1538,15 +1543,14 @@ module Sequel
1538
1543
 
1539
1544
  def select_with_sql(sql)
1540
1545
  return unless supports_cte?
1541
- ws = opts[:with]
1542
- return if !ws || ws.empty?
1546
+ ctes = opts[:with]
1547
+ return if !ctes || ctes.empty?
1543
1548
  sql << select_with_sql_base
1544
1549
  c = false
1545
1550
  comma = ', '
1546
- ws.each do |w|
1551
+ ctes.each do |cte|
1547
1552
  sql << comma if c
1548
- select_with_sql_prefix(sql, w)
1549
- literal_dataset_append(sql, w[:dataset])
1553
+ select_with_sql_cte(sql, cte)
1550
1554
  c ||= true
1551
1555
  end
1552
1556
  sql << ' '
@@ -1559,6 +1563,11 @@ module Sequel
1559
1563
  "WITH "
1560
1564
  end
1561
1565
 
1566
+ def select_with_sql_cte(sql, cte)
1567
+ select_with_sql_prefix(sql, cte)
1568
+ literal_dataset_append(sql, cte[:dataset])
1569
+ end
1570
+
1562
1571
  def select_with_sql_prefix(sql, w)
1563
1572
  quote_identifier_append(sql, w[:name])
1564
1573
  if args = w[:args]
@@ -33,7 +33,7 @@ module Sequel
33
33
  module AnyNotEmpty
34
34
  # If a block is not given, return whether the dataset is not empty.
35
35
  def any?
36
- if block_given?
36
+ if defined?(yield)
37
37
  super
38
38
  else
39
39
  !empty?
@@ -15,6 +15,12 @@ raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VE
15
15
  # :nocov:
16
16
 
17
17
  module Sequel::CoreRefinements
18
+ # :nocov:
19
+ include_meth = RUBY_VERSION >= '3.1' ? :import_methods : :include
20
+ # :nocov:
21
+ INCLUDE_METH = include_meth
22
+ private_constant :INCLUDE_METH
23
+
18
24
  refine Array do
19
25
  # Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, not matching all of the
20
26
  # conditions.
@@ -161,8 +167,8 @@ module Sequel::CoreRefinements
161
167
  end
162
168
 
163
169
  refine String do
164
- include Sequel::SQL::AliasMethods
165
- include Sequel::SQL::CastMethods
170
+ send include_meth, Sequel::SQL::AliasMethods
171
+ send include_meth, Sequel::SQL::CastMethods
166
172
 
167
173
  # Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
168
174
  # literalization, e.g.:
@@ -189,15 +195,34 @@ module Sequel::CoreRefinements
189
195
  end
190
196
 
191
197
  refine Symbol do
192
- include Sequel::SQL::AliasMethods
193
- include Sequel::SQL::CastMethods
194
- include Sequel::SQL::OrderMethods
195
- include Sequel::SQL::BooleanMethods
196
- include Sequel::SQL::NumericMethods
197
- include Sequel::SQL::QualifyingMethods
198
- include Sequel::SQL::StringMethods
199
- include Sequel::SQL::SubscriptMethods
200
- include Sequel::SQL::ComplexExpressionMethods
198
+ send include_meth, Sequel::SQL::AliasMethods
199
+ send include_meth, Sequel::SQL::CastMethods
200
+ send include_meth, Sequel::SQL::OrderMethods
201
+ send include_meth, Sequel::SQL::BooleanMethods
202
+ send include_meth, Sequel::SQL::NumericMethods
203
+
204
+ # :nocov:
205
+ remove_method :* if RUBY_VERSION >= '3.1'
206
+ # :nocov:
207
+
208
+ send include_meth, Sequel::SQL::QualifyingMethods
209
+ send include_meth, Sequel::SQL::StringMethods
210
+ send include_meth, Sequel::SQL::SubscriptMethods
211
+ send include_meth, Sequel::SQL::ComplexExpressionMethods
212
+
213
+ # :nocov:
214
+ if RUBY_VERSION >= '3.1'
215
+ remove_method :*
216
+ def *(ce=(arg=false;nil))
217
+ if arg == false
218
+ Sequel::SQL::ColumnAll.new(self)
219
+ else
220
+ Sequel::SQL::NumericExpression.new(:*, self, ce)
221
+ end
222
+ end
223
+
224
+ end
225
+ # :nocov:
201
226
 
202
227
  # Returns receiver wrapped in an <tt>Sequel::SQL::Identifier</tt>.
203
228
  #
@@ -0,0 +1,67 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The date_parse_input_handler extension allows for configuring how input
4
+ # to date parsing methods should be handled. By default, the
5
+ # extension does not change behavior. However, you can use the
6
+ # +Sequel.date_parse_input_handler+ method to support custom handling
7
+ # of input strings to the date parsing methods. For example, if you want
8
+ # to implement a length check to prevent denial of service vulnerabilities
9
+ # in older versions of Ruby, you can do:
10
+ #
11
+ # Sequel.extension :date_parse_input_handler
12
+ # Sequel.date_parse_input_handler do |string|
13
+ # raise Sequel::InvalidValue, "string length (200) exceeds the limit 128" if string.bytesize > 128
14
+ # string
15
+ # end
16
+ #
17
+ # You can also use +Sequel.date_parse_input_handler+ to modify the string
18
+ # that will be passed to the parsing methods. For example, you could
19
+ # truncate it:
20
+ #
21
+ # Sequel.date_parse_input_handler do |string|
22
+ # string.b[0, 128]
23
+ # end
24
+ #
25
+ # Be aware that modern versions of Ruby will raise an exception if
26
+ # date parsing input exceeds 128 bytes.
27
+
28
+ module Sequel
29
+ module DateParseInputHandler
30
+ def date_parse_input_handler(&block)
31
+ singleton_class.class_eval do
32
+ define_method(:handle_date_parse_input, &block)
33
+ private :handle_date_parse_input
34
+ alias handle_date_parse_input handle_date_parse_input
35
+ end
36
+ end
37
+
38
+ # Call date parse input handler with input string.
39
+ def string_to_date(string)
40
+ super(handle_date_parse_input(string))
41
+ end
42
+
43
+ # Call date parse input handler with input string.
44
+ def string_to_datetime(string)
45
+ super(handle_date_parse_input(string))
46
+ end
47
+
48
+ # Call date parse input handler with input string.
49
+ def string_to_time(string)
50
+ super(handle_date_parse_input(string))
51
+ end
52
+
53
+ private
54
+
55
+ # Call date parse input handler with input string.
56
+ def _date_parse(string)
57
+ super(handle_date_parse_input(string))
58
+ end
59
+
60
+ # Return string as-is by default, so by default behavior does not change.
61
+ def handle_date_parse_input(string)
62
+ string
63
+ end
64
+ end
65
+
66
+ extend DateParseInputHandler
67
+ end
@@ -19,7 +19,11 @@ module Sequel::DateTimeParseToTime
19
19
  # Use DateTime.parse.to_time to do the conversion if the input a string and is assumed to
20
20
  # be in UTC and there is no offset information in the string.
21
21
  def convert_input_timestamp(v, input_timezone)
22
- if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !Date._parse(v).has_key?(:offset)
22
+ if v.is_a?(String) && datetime_class == Time && input_timezone == :utc && !_date_parse(v).has_key?(:offset)
23
+ # :nocov:
24
+ # Whether this is fully branch covered depends on the order in which the specs are run.
25
+ v = handle_date_parse_input(v) if respond_to?(:handle_date_parse_input, true)
26
+ # :nocov:
23
27
  t = DateTime.parse(v).to_time
24
28
  case application_timezone
25
29
  when nil, :local
@@ -44,7 +44,7 @@ module Sequel
44
44
  # :nocov:
45
45
 
46
46
  # Customize handling of duplicate columns for this dataset.
47
- def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless block_given?; nil), &block)
47
+ def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
48
48
  raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
49
49
  clone(:on_duplicate_columns=>handler||block)
50
50
  end
@@ -102,7 +102,7 @@ class String
102
102
  # Yield the Inflections module if a block is given, and return
103
103
  # the Inflections module.
104
104
  def self.inflections
105
- yield Inflections if block_given?
105
+ yield Inflections if defined?(yield)
106
106
  Inflections
107
107
  end
108
108
 
@@ -388,6 +388,9 @@ module Sequel
388
388
 
389
389
  # Migrates the supplied database using the migration files in the specified directory. Options:
390
390
  # :allow_missing_migration_files :: Don't raise an error if there are missing migration files.
391
+ # It is very risky to use this option, since it can result in
392
+ # the database schema version number not matching the expected
393
+ # database schema.
391
394
  # :column :: The column in the :table argument storing the migration version (default: :version).
392
395
  # :current :: The current version of the database. If not given, it is retrieved from the database
393
396
  # using the :table and :column options.
@@ -542,7 +545,7 @@ module Sequel
542
545
 
543
546
  @direction = current < target ? :up : :down
544
547
 
545
- if @direction == :down && @current >= @files.length
548
+ if @direction == :down && @current >= @files.length && !@allow_missing_migration_files
546
549
  raise Migrator::Error, "Missing migration version(s) needed to migrate down to target version (current: #{current}, target: #{target})"
547
550
  end
548
551
 
@@ -54,7 +54,7 @@ module Sequel
54
54
  # an enumerator if no block is given.
55
55
  def each_page(page_size)
56
56
  raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
57
- return to_enum(:each_page, page_size) unless block_given?
57
+ return to_enum(:each_page, page_size) unless defined?(yield)
58
58
  record_count = count
59
59
  total_pages = (record_count / page_size.to_f).ceil
60
60
  (1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
@@ -329,7 +329,7 @@ end
329
329
  if defined?(Sequel::CoreRefinements)
330
330
  module Sequel::CoreRefinements
331
331
  refine Symbol do
332
- include Sequel::Postgres::ArrayOpMethods
332
+ send INCLUDE_METH, Sequel::Postgres::ArrayOpMethods
333
333
  end
334
334
  end
335
335
  end
@@ -83,7 +83,7 @@ module Sequel
83
83
  # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
84
84
  # value based on the convert_infinite_timestamps setting.
85
85
  def to_application_timestamp(value)
86
- if value.is_a?(String) && (m = value.match(/((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/)) && (m[2] || m[3])
86
+ if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
87
87
  if m[3]
88
88
  value = value.sub(' BC', '').sub(' ', ' BC ')
89
89
  conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'