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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df46d23e3607466eb34477a5ba9c31d14fd57294275088599f8ef4d561179faf
4
- data.tar.gz: 9566fcb614fded77e5e20c929c9ed5cb6786e0548c3e59837ae2dd1a1877762d
3
+ metadata.gz: dd35e8223b7f6d450cf4c1efeaad152236b4a1e360299aa46b03841bb45996c6
4
+ data.tar.gz: '096a62cc241cf6f48d0c2b3e0799402f21aa24a1196a332e022ef53ebaaeea85'
5
5
  SHA512:
6
- metadata.gz: 8375cb4204be00b492b841fea5427dce809e43d119c6fd9f559cbfa1b4b9f652e752d22e52a93942a8a228166829d4227202e0f783a89f790d4fad40a8eb0963
7
- data.tar.gz: 4c6bbc4bd1172ed8c5117bd628651757b8685fab754b4d664ceb3254917fbeb58d778bccd3b8dbe998e4694f185baa4e630910dc1de8fe1b5026d23e95c215b4
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 = 150000
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 as a string
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
- with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
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 prepared statement, should be one of :select, :first,
95
- # :insert, :update, :delete, or :single_value
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 prepared_type
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
- all(&block)
193
+ with_sql_all(prepared_sql, &block)
188
194
  when :each
189
- each(&block)
190
- when :insert_select
191
- with_sql(prepared_sql).first
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 prepared_type == :delete
198
- delete
201
+ elsif type == :delete
202
+ with_sql_delete(prepared_sql)
199
203
  else
200
- public_send(prepared_type, *prepared_modify_values)
204
+ force_prepared_sql.public_send(type, *prepared_modify_values)
201
205
  end
202
- when :insert_pk
203
- fetch_rows(prepared_sql){|r| return r.values.first}
206
+ when :insert_pk, :single_value
207
+ with_sql_single_value(prepared_sql)
204
208
  when Array
205
209
  # :nocov:
206
- case prepared_type[0]
210
+ case type[0]
207
211
  # :nocov:
208
212
  when :map, :as_hash, :to_hash, :to_hash_groups
209
- public_send(*prepared_type, &block)
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
- case type
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(:"#{type}_sql", *values)
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 (:select, :first, :insert, :insert_select, :update, :delete, or :single_value),
348
- # run the sql with the bind variables specified in the hash. +values+ is a hash passed to
349
- # insert or update (if one of those types is used), which may contain placeholders.
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
@@ -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
- schema, table = schema_and_table(table)
704
- if schema
705
- quote_identifier_append(sql, schema)
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
- case identifier
1737
- when SQL::Identifier
1738
- identifier.value.to_s
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
@@ -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 = 92
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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.92.0
4
+ version: 5.93.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans