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.
- checksums.yaml +4 -4
- data/CHANGELOG +80 -0
- data/README.rdoc +12 -5
- data/doc/migration.rdoc +1 -1
- data/doc/opening_databases.rdoc +1 -1
- data/doc/postgresql.rdoc +8 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/testing.rdoc +3 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +3 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +9 -11
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +42 -44
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +2 -0
- data/lib/sequel/adapters/shared/mysql.rb +4 -2
- data/lib/sequel/adapters/shared/postgres.rb +59 -6
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +16 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/dataset/actions.rb +2 -2
- data/lib/sequel/dataset/query.rb +45 -3
- data/lib/sequel/dataset/sql.rb +18 -9
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/migration.rb +4 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array_ops.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +1 -0
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +71 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +4 -12
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +108 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/model/associations.rb +3 -1
- data/lib/sequel/model/base.rb +9 -13
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_validations.rb +25 -5
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/composition.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/unused_associations.rb +2 -2
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +7 -1
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- 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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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.
|
@@ -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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
28
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
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
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
310
|
-
|
311
|
-
|
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
|
58
|
+
if defined?(yield)
|
59
59
|
return yield(db)
|
60
60
|
end
|
61
61
|
ensure
|
62
|
-
if
|
62
|
+
if defined?(yield)
|
63
63
|
db.disconnect if db
|
64
64
|
Sequel.synchronize{::Sequel::DATABASES.delete(db)}
|
65
65
|
end
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -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)
|
@@ -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 =>
|
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
|
549
|
+
unless defined?(yield)
|
550
550
|
return enum_for(:paged_each, opts)
|
551
551
|
end
|
552
552
|
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -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
|
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.
|
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)
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -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
|
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
|
-
|
1542
|
-
return if !
|
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
|
-
|
1551
|
+
ctes.each do |cte|
|
1547
1552
|
sql << comma if c
|
1548
|
-
|
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]
|
@@ -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
|
-
|
165
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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 && !
|
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
|
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
|
@@ -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
|
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)}
|
@@ -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 =
|
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'
|