sequel 5.92.0 → 5.93.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/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/shared/postgres.rb +100 -3
- data/lib/sequel/dataset/prepared_statements.rb +68 -24
- data/lib/sequel/dataset/sql.rb +7 -4
- data/lib/sequel/extensions/pg_auto_parameterize.rb +5 -0
- data/lib/sequel/sql.rb +7 -5
- data/lib/sequel/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd35e8223b7f6d450cf4c1efeaad152236b4a1e360299aa46b03841bb45996c6
|
4
|
+
data.tar.gz: '096a62cc241cf6f48d0c2b3e0799402f21aa24a1196a332e022ef53ebaaeea85'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b66c7e30dfb69468ad41bfe64de4e0442ad62590ea735b2e4bb591c46b949794a7a453c21532999c89296694f0136d9353041ec56fc470ab2ba0483f0534d61
|
7
|
+
data.tar.gz: 978dcd457829ff39f1ae9769dc574049cf29d07a2dbc8e307aa806a7b1954cd72432257de37d9e5236269b5746f615f8d0f7feb4979e9b86b619f582344afc29
|
@@ -353,6 +353,20 @@ module Sequel
|
|
353
353
|
i = prepared_args.length
|
354
354
|
LiteralString.new(":#{i}")
|
355
355
|
end
|
356
|
+
|
357
|
+
# Avoid infinite recursion on Oracle <12 for datasets with limits
|
358
|
+
# (which are implemented via subqueries). If the given dataset's
|
359
|
+
# prepared args are the same object as current dataset's, call the
|
360
|
+
# standard Sequel::Dataset#subselect_sql_append method, instead
|
361
|
+
# of calling super (which will call prepared_sql and result in
|
362
|
+
# infinite recursion).
|
363
|
+
def subselect_sql_append(sql, ds)
|
364
|
+
if !supports_fetch_next_rows? && ds.opts[:prepared_args].equal?(@opts[:prepared_args])
|
365
|
+
orig_subselect_sql_append(sql, ds)
|
366
|
+
else
|
367
|
+
super
|
368
|
+
end
|
369
|
+
end
|
356
370
|
end
|
357
371
|
|
358
372
|
BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
|
@@ -383,6 +397,8 @@ module Sequel
|
|
383
397
|
|
384
398
|
private
|
385
399
|
|
400
|
+
alias orig_subselect_sql_append subselect_sql_append
|
401
|
+
|
386
402
|
def literal_other_append(sql, v)
|
387
403
|
case v
|
388
404
|
when OraDate
|
@@ -101,7 +101,7 @@ module Sequel
|
|
101
101
|
|
102
102
|
def self.mock_adapter_setup(db)
|
103
103
|
db.instance_exec do
|
104
|
-
@server_version =
|
104
|
+
@server_version = 170000
|
105
105
|
initialize_postgres_adapter
|
106
106
|
extend(MockAdapterDatabaseMethods)
|
107
107
|
end
|
@@ -1888,9 +1888,48 @@ module Sequel
|
|
1888
1888
|
super
|
1889
1889
|
end
|
1890
1890
|
|
1891
|
-
# Return the results of an EXPLAIN query
|
1891
|
+
# Return the results of an EXPLAIN query. Boolean options:
|
1892
|
+
#
|
1893
|
+
# :analyze :: Use the ANALYZE option.
|
1894
|
+
# :buffers :: Use the BUFFERS option.
|
1895
|
+
# :costs :: Use the COSTS option.
|
1896
|
+
# :generic_plan :: Use the GENERIC_PLAN option.
|
1897
|
+
# :memory :: Use the MEMORY option.
|
1898
|
+
# :settings :: Use the SETTINGS option.
|
1899
|
+
# :summary :: Use the SUMMARY option.
|
1900
|
+
# :timing :: Use the TIMING option.
|
1901
|
+
# :verbose :: Use the VERBOSE option.
|
1902
|
+
# :wal :: Use the WAL option.
|
1903
|
+
#
|
1904
|
+
# Non boolean options:
|
1905
|
+
#
|
1906
|
+
# :format :: Use the FORMAT option to change the format of the
|
1907
|
+
# returned value. Values can be :text, :xml, :json,
|
1908
|
+
# or :yaml.
|
1909
|
+
# :serialize :: Use the SERIALIZE option to get timing on
|
1910
|
+
# serialization. Values can be :none, :text, or
|
1911
|
+
# :binary.
|
1912
|
+
#
|
1913
|
+
# See the PostgreSQL EXPLAIN documentation for an explanation of
|
1914
|
+
# what each option does.
|
1915
|
+
#
|
1916
|
+
# In most cases, the return value is a single string. However,
|
1917
|
+
# using the <tt>format: :json</tt> option can result in the return
|
1918
|
+
# value being an array containing a hash.
|
1892
1919
|
def explain(opts=OPTS)
|
1893
|
-
|
1920
|
+
rows = clone(:append_sql=>explain_sql_string_origin(opts)).map(:'QUERY PLAN')
|
1921
|
+
|
1922
|
+
if rows.length == 1
|
1923
|
+
rows[0]
|
1924
|
+
elsif rows.all?{|row| String === row}
|
1925
|
+
rows.join("\r\n")
|
1926
|
+
# :nocov:
|
1927
|
+
else
|
1928
|
+
# This branch is unreachable in tests, but it seems better to just return
|
1929
|
+
# all rows than throw in error if this case actually happens.
|
1930
|
+
rows
|
1931
|
+
# :nocov:
|
1932
|
+
end
|
1894
1933
|
end
|
1895
1934
|
|
1896
1935
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
@@ -2417,7 +2456,65 @@ module Sequel
|
|
2417
2456
|
c ||= true
|
2418
2457
|
end
|
2419
2458
|
end
|
2459
|
+
|
2460
|
+
EXPLAIN_BOOLEAN_OPTIONS = {}
|
2461
|
+
%w[analyze verbose costs settings generic_plan buffers wal timing summary memory].each do |str|
|
2462
|
+
EXPLAIN_BOOLEAN_OPTIONS[str.to_sym] = str.upcase.freeze
|
2463
|
+
end
|
2464
|
+
EXPLAIN_BOOLEAN_OPTIONS.freeze
|
2465
|
+
|
2466
|
+
EXPLAIN_NONBOOLEAN_OPTIONS = {
|
2467
|
+
:serialize => {:none=>"SERIALIZE NONE", :text=>"SERIALIZE TEXT", :binary=>"SERIALIZE BINARY"}.freeze,
|
2468
|
+
:format => {:text=>"FORMAT TEXT", :xml=>"FORMAT XML", :json=>"FORMAT JSON", :yaml=>"FORMAT YAML"}.freeze
|
2469
|
+
}.freeze
|
2420
2470
|
|
2471
|
+
# A mutable string used as the prefix when explaining a query.
|
2472
|
+
def explain_sql_string_origin(opts)
|
2473
|
+
origin = String.new
|
2474
|
+
origin << 'EXPLAIN '
|
2475
|
+
|
2476
|
+
# :nocov:
|
2477
|
+
if server_version < 90000
|
2478
|
+
if opts[:analyze]
|
2479
|
+
origin << 'ANALYZE '
|
2480
|
+
end
|
2481
|
+
|
2482
|
+
return origin
|
2483
|
+
end
|
2484
|
+
# :nocov:
|
2485
|
+
|
2486
|
+
comma = nil
|
2487
|
+
paren = "("
|
2488
|
+
|
2489
|
+
add_opt = lambda do |str, value|
|
2490
|
+
origin << paren if paren
|
2491
|
+
origin << comma if comma
|
2492
|
+
origin << str
|
2493
|
+
origin << " FALSE" unless value
|
2494
|
+
comma ||= ', '
|
2495
|
+
paren &&= nil
|
2496
|
+
end
|
2497
|
+
|
2498
|
+
EXPLAIN_BOOLEAN_OPTIONS.each do |key, str|
|
2499
|
+
unless (value = opts[key]).nil?
|
2500
|
+
add_opt.call(str, value)
|
2501
|
+
end
|
2502
|
+
end
|
2503
|
+
|
2504
|
+
EXPLAIN_NONBOOLEAN_OPTIONS.each do |key, e_opts|
|
2505
|
+
if value = opts[key]
|
2506
|
+
if str = e_opts[value]
|
2507
|
+
add_opt.call(str, true)
|
2508
|
+
else
|
2509
|
+
raise Sequel::Error, "unrecognized value for Dataset#explain #{key.inspect} option: #{value.inspect}"
|
2510
|
+
end
|
2511
|
+
end
|
2512
|
+
end
|
2513
|
+
|
2514
|
+
origin << ') ' unless paren
|
2515
|
+
origin
|
2516
|
+
end
|
2517
|
+
|
2421
2518
|
# Add ON CONFLICT clause if it should be used
|
2422
2519
|
def insert_conflict_sql(sql)
|
2423
2520
|
if opts = @opts[:insert_conflict]
|
@@ -91,8 +91,14 @@ module Sequel
|
|
91
91
|
@opts[:log_sql]
|
92
92
|
end
|
93
93
|
|
94
|
-
# The type of
|
95
|
-
#
|
94
|
+
# The type of SQL to generate for the prepared statement. Generally
|
95
|
+
# the same as #prepared_type, but can be different.
|
96
|
+
def prepared_sql_type
|
97
|
+
@opts[:prepared_sql_type] || prepared_type
|
98
|
+
end
|
99
|
+
|
100
|
+
# The type of prepared statement, which controls how the prepared statement
|
101
|
+
# handles results from the database.
|
96
102
|
def prepared_type
|
97
103
|
@opts[:prepared_type]
|
98
104
|
end
|
@@ -141,7 +147,7 @@ module Sequel
|
|
141
147
|
# Returns the SQL for the prepared statement, depending on
|
142
148
|
# the type of the statement and the prepared_modify_values.
|
143
149
|
def prepared_sql
|
144
|
-
case
|
150
|
+
case prepared_sql_type
|
145
151
|
when :select, :all, :each
|
146
152
|
# Most common scenario, so listed first.
|
147
153
|
select_sql
|
@@ -182,34 +188,30 @@ module Sequel
|
|
182
188
|
|
183
189
|
# Run the method based on the type of prepared statement.
|
184
190
|
def run(&block)
|
185
|
-
case prepared_type
|
191
|
+
case type = prepared_type
|
186
192
|
when :select, :all
|
187
|
-
|
193
|
+
with_sql_all(prepared_sql, &block)
|
188
194
|
when :each
|
189
|
-
|
190
|
-
when :insert_select
|
191
|
-
|
192
|
-
when :first
|
193
|
-
first
|
195
|
+
with_sql_each(prepared_sql, &block)
|
196
|
+
when :insert_select, :first
|
197
|
+
with_sql_first(prepared_sql)
|
194
198
|
when :insert, :update, :delete
|
195
199
|
if opts[:returning] && supports_returning?(prepared_type)
|
196
200
|
returning_fetch_rows(prepared_sql)
|
197
|
-
elsif
|
198
|
-
|
201
|
+
elsif type == :delete
|
202
|
+
with_sql_delete(prepared_sql)
|
199
203
|
else
|
200
|
-
public_send(
|
204
|
+
force_prepared_sql.public_send(type, *prepared_modify_values)
|
201
205
|
end
|
202
|
-
when :insert_pk
|
203
|
-
|
206
|
+
when :insert_pk, :single_value
|
207
|
+
with_sql_single_value(prepared_sql)
|
204
208
|
when Array
|
205
209
|
# :nocov:
|
206
|
-
case
|
210
|
+
case type[0]
|
207
211
|
# :nocov:
|
208
212
|
when :map, :as_hash, :to_hash, :to_hash_groups
|
209
|
-
public_send(*
|
213
|
+
force_prepared_sql.public_send(*type, &block)
|
210
214
|
end
|
211
|
-
when :single_value
|
212
|
-
single_value
|
213
215
|
else
|
214
216
|
raise Error, "unsupported prepared statement type used: #{prepared_type.inspect}"
|
215
217
|
end
|
@@ -217,6 +219,16 @@ module Sequel
|
|
217
219
|
|
218
220
|
private
|
219
221
|
|
222
|
+
# If the prepared_sql_type does not match the prepared statement, return a clone that
|
223
|
+
# with the prepared SQL, to ensure the prepared_sql_type is respected.
|
224
|
+
def force_prepared_sql
|
225
|
+
if prepared_sql_type != prepared_type
|
226
|
+
with_sql(prepared_sql)
|
227
|
+
else
|
228
|
+
self
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
220
232
|
# Returns the value of the prepared_args hash for the given key.
|
221
233
|
def prepared_arg(k)
|
222
234
|
@opts[:bind_vars][k]
|
@@ -294,11 +306,12 @@ module Sequel
|
|
294
306
|
prepared_sql, frags = Sequel::Dataset::PlaceholderLiteralizer::Recorder.new.send(:prepared_sql_and_frags, self, prepared_args) do |pl, ds|
|
295
307
|
ds = ds.clone(:recorder=>pl)
|
296
308
|
|
297
|
-
|
309
|
+
sql_type = prepared_sql_type || type
|
310
|
+
case sql_type
|
298
311
|
when :first, :single_value
|
299
312
|
ds.limit(1)
|
300
313
|
when :update, :insert, :insert_select, :delete
|
301
|
-
ds.with_sql(:"#{
|
314
|
+
ds.with_sql(:"#{sql_type}_sql", *values)
|
302
315
|
when :insert_pk
|
303
316
|
ds.with_sql(:insert_sql, *values)
|
304
317
|
else
|
@@ -344,13 +357,23 @@ module Sequel
|
|
344
357
|
clone(:bind_vars=>bind_vars)
|
345
358
|
end
|
346
359
|
|
347
|
-
# For the given type
|
348
|
-
#
|
349
|
-
#
|
360
|
+
# For the given type, run the sql with the bind variables specified in the hash.
|
361
|
+
# +values+ is a hash passed to insert or update (if one of those types is used),
|
362
|
+
# which may contain placeholders.
|
363
|
+
#
|
364
|
+
# The following types are supported:
|
365
|
+
#
|
366
|
+
# * :select, :all, :each, :first, :single_value, :insert, :insert_select, :insert_pk, :update, :delete
|
367
|
+
# * Array where first element is :map, :as_hash, :to_hash, :to_hash_groups (remaining elements
|
368
|
+
# are passed to the related method)
|
350
369
|
#
|
351
370
|
# DB[:table].where(id: :$id).call(:first, id: 1)
|
352
371
|
# # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
|
353
372
|
# # => {:id=>1}
|
373
|
+
#
|
374
|
+
# DB[:table].where(id: :$id).call(:update, {c: 1, id: 2}, col: :$c)
|
375
|
+
# # UPDATE table WHERE id = ? SET col = ? -- (2, 1)
|
376
|
+
# # => 1
|
354
377
|
def call(type, bind_variables=OPTS, *values, &block)
|
355
378
|
to_prepared_statement(type, values, :extend=>bound_variable_modules).call(bind_variables, &block)
|
356
379
|
end
|
@@ -371,6 +394,14 @@ module Sequel
|
|
371
394
|
# # => {:id=>1, :name=>'Blah'}
|
372
395
|
#
|
373
396
|
# DB.call(:select_by_name, name: 'Blah') # Same thing
|
397
|
+
#
|
398
|
+
# +values+ given are passed to +insert+ or +update+ if they are used:
|
399
|
+
#
|
400
|
+
# ps = DB[:table].where(id: :$i).prepare(:update, :update_name, name: :$n)
|
401
|
+
#
|
402
|
+
# ps.call(i: 1, n: 'Blah')
|
403
|
+
# # UPDATE table WHERE id = ? SET name = ? -- (1, 'Blah')
|
404
|
+
# # => 1
|
374
405
|
def prepare(type, name, *values)
|
375
406
|
ps = to_prepared_statement(type, values, :name=>name, :extend=>prepared_statement_modules, :no_delayed_evaluations=>true)
|
376
407
|
|
@@ -387,6 +418,19 @@ module Sequel
|
|
387
418
|
ps
|
388
419
|
end
|
389
420
|
|
421
|
+
# Set the type of SQL to use for prepared statements based on this
|
422
|
+
# dataset. Prepared statements default to using the same SQL type
|
423
|
+
# as the type that is passed to #prepare/#call, but there are cases
|
424
|
+
# where it is helpful to use a different SQL type.
|
425
|
+
#
|
426
|
+
# Available types are: :select, :first, :single_value, :update,
|
427
|
+
# :delete, :insert, :insert_select, :insert_pk
|
428
|
+
#
|
429
|
+
# Other types are treated as :select.
|
430
|
+
def prepare_sql_type(type)
|
431
|
+
clone(:prepared_sql_type => type)
|
432
|
+
end
|
433
|
+
|
390
434
|
protected
|
391
435
|
|
392
436
|
# Return a cloned copy of the current dataset extended with
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -685,10 +685,10 @@ module Sequel
|
|
685
685
|
# being quoted, returns name as a string. If identifiers are being quoted
|
686
686
|
# quote the name with quoted_identifier.
|
687
687
|
def quote_identifier_append(sql, name)
|
688
|
+
name = name.value if name.is_a?(SQL::Identifier)
|
688
689
|
if name.is_a?(LiteralString)
|
689
690
|
sql << name
|
690
691
|
else
|
691
|
-
name = name.value if name.is_a?(SQL::Identifier)
|
692
692
|
name = input_identifier(name)
|
693
693
|
if quote_identifiers?
|
694
694
|
quoted_identifier_append(sql, name)
|
@@ -700,11 +700,14 @@ module Sequel
|
|
700
700
|
|
701
701
|
# Append literalization of identifier or unqualified identifier to SQL string.
|
702
702
|
def quote_schema_table_append(sql, table)
|
703
|
-
|
704
|
-
|
705
|
-
|
703
|
+
qualifiers = split_qualifiers(table)
|
704
|
+
table = qualifiers.pop
|
705
|
+
|
706
|
+
qualifiers.each do |q|
|
707
|
+
quote_identifier_append(sql, q)
|
706
708
|
sql << '.'
|
707
709
|
end
|
710
|
+
|
708
711
|
quote_identifier_append(sql, table)
|
709
712
|
end
|
710
713
|
|
@@ -463,6 +463,11 @@ module Sequel
|
|
463
463
|
@opts[:no_auto_parameterize] ? super : QueryString.new
|
464
464
|
end
|
465
465
|
|
466
|
+
# A mutable string used as the prefix when explaining a query.
|
467
|
+
def explain_sql_string_origin(opts)
|
468
|
+
@opts[:no_auto_parameterize] ? super : (QueryString.new << super)
|
469
|
+
end
|
470
|
+
|
466
471
|
# If subquery uses with_sql with a method name symbol, get the dataset
|
467
472
|
# with_sql was called on, and use that as the subquery, recording the
|
468
473
|
# arguments to with_sql that will be used to calculate the sql.
|
data/lib/sequel/sql.rb
CHANGED
@@ -1733,12 +1733,11 @@ module Sequel
|
|
1733
1733
|
|
1734
1734
|
# Automatically convert SQL::Identifiers to strings
|
1735
1735
|
def convert_identifier(identifier)
|
1736
|
-
|
1737
|
-
|
1738
|
-
identifier.
|
1739
|
-
else
|
1740
|
-
identifier
|
1736
|
+
if identifier.is_a?(SQL::Identifier)
|
1737
|
+
identifier = identifier.value
|
1738
|
+
identifier = identifier.to_s unless identifier.is_a?(LiteralString)
|
1741
1739
|
end
|
1740
|
+
identifier
|
1742
1741
|
end
|
1743
1742
|
end
|
1744
1743
|
|
@@ -2053,6 +2052,9 @@ module Sequel
|
|
2053
2052
|
end
|
2054
2053
|
end
|
2055
2054
|
|
2055
|
+
SQL::Constants::OLD = SQL::Identifier.new(LiteralString.new("OLD").freeze)
|
2056
|
+
SQL::Constants::NEW = SQL::Identifier.new(LiteralString.new("NEW").freeze)
|
2057
|
+
|
2056
2058
|
include SQL::Constants
|
2057
2059
|
extend SQL::Builders
|
2058
2060
|
extend SQL::OperatorBuilders
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 93
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|