sequel 5.85.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/ado.rb +1 -1
  3. data/lib/sequel/adapters/ibmdb.rb +1 -0
  4. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  5. data/lib/sequel/adapters/jdbc/derby.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  7. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  9. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  10. data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
  11. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  12. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  13. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
  14. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  15. data/lib/sequel/adapters/jdbc.rb +8 -8
  16. data/lib/sequel/adapters/mysql2.rb +8 -1
  17. data/lib/sequel/adapters/oracle.rb +16 -0
  18. data/lib/sequel/adapters/shared/access.rb +1 -0
  19. data/lib/sequel/adapters/shared/mssql.rb +4 -3
  20. data/lib/sequel/adapters/shared/mysql.rb +8 -4
  21. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  22. data/lib/sequel/adapters/shared/postgres.rb +140 -9
  23. data/lib/sequel/adapters/sqlite.rb +4 -0
  24. data/lib/sequel/adapters/trilogy.rb +1 -2
  25. data/lib/sequel/core.rb +15 -0
  26. data/lib/sequel/database/dataset_defaults.rb +3 -3
  27. data/lib/sequel/database/misc.rb +17 -4
  28. data/lib/sequel/database/query.rb +11 -11
  29. data/lib/sequel/database/schema_generator.rb +8 -0
  30. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  31. data/lib/sequel/dataset/prepared_statements.rb +70 -25
  32. data/lib/sequel/dataset/query.rb +9 -5
  33. data/lib/sequel/dataset/sql.rb +19 -9
  34. data/lib/sequel/extensions/connection_validator.rb +15 -10
  35. data/lib/sequel/extensions/migration.rb +23 -3
  36. data/lib/sequel/extensions/null_dataset.rb +2 -2
  37. data/lib/sequel/extensions/pg_auto_parameterize.rb +6 -1
  38. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
  39. data/lib/sequel/extensions/pg_enum.rb +3 -3
  40. data/lib/sequel/extensions/pg_row.rb +3 -1
  41. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  42. data/lib/sequel/extensions/query_blocker.rb +172 -0
  43. data/lib/sequel/extensions/schema_caching.rb +24 -9
  44. data/lib/sequel/extensions/schema_dumper.rb +16 -4
  45. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  46. data/lib/sequel/extensions/string_agg.rb +2 -2
  47. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  48. data/lib/sequel/model/associations.rb +28 -3
  49. data/lib/sequel/model/base.rb +67 -18
  50. data/lib/sequel/plugins/composition.rb +1 -1
  51. data/lib/sequel/plugins/enum.rb +1 -1
  52. data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
  53. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  54. data/lib/sequel/plugins/instance_filters.rb +4 -1
  55. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  56. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  57. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  58. data/lib/sequel/plugins/paged_operations.rb +5 -2
  59. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  60. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  61. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  62. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  63. data/lib/sequel/plugins/serialization.rb +11 -5
  64. data/lib/sequel/plugins/sql_comments.rb +7 -2
  65. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  66. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  67. data/lib/sequel/plugins/subset_static_cache.rb +263 -0
  68. data/lib/sequel/sql.rb +15 -6
  69. data/lib/sequel/version.rb +1 -1
  70. metadata +9 -6
@@ -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
@@ -1075,6 +1075,7 @@ module Sequel
1075
1075
  pg_attribute[:attname].as(:name),
1076
1076
  SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1077
1077
  SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1078
+ SQL::Function.new(:col_description, pg_class[:oid], pg_attribute[:attnum]).as(:comment),
1078
1079
  SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1079
1080
  SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1080
1081
  SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
@@ -1270,21 +1271,32 @@ module Sequel
1270
1271
 
1271
1272
  # Handle exclusion constraints.
1272
1273
  def constraint_definition_sql(constraint)
1273
- case constraint[:type]
1274
+ case type = constraint[:type]
1274
1275
  when :exclude
1275
1276
  elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
1276
1277
  sql = String.new
1277
1278
  sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
1278
1279
  constraint_deferrable_sql_append(sql, constraint[:deferrable])
1279
1280
  sql
1280
- when :foreign_key, :check
1281
+ when :primary_key, :unique
1282
+ if using_index = constraint[:using_index]
1283
+ sql = String.new
1284
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1285
+ if type == :primary_key
1286
+ sql << primary_key_constraint_sql_fragment(constraint)
1287
+ else
1288
+ sql << unique_constraint_sql_fragment(constraint)
1289
+ end
1290
+ sql << " USING INDEX " << quote_identifier(using_index)
1291
+ else
1292
+ super
1293
+ end
1294
+ else # when :foreign_key, :check
1281
1295
  sql = super
1282
1296
  if constraint[:not_valid]
1283
1297
  sql << " NOT VALID"
1284
1298
  end
1285
1299
  sql
1286
- else
1287
- super
1288
1300
  end
1289
1301
  end
1290
1302
 
@@ -1651,9 +1663,9 @@ module Sequel
1651
1663
  # Handle interval and citext types.
1652
1664
  def schema_column_type(db_type)
1653
1665
  case db_type
1654
- when /\Ainterval\z/io
1666
+ when /\Ainterval\z/i
1655
1667
  :interval
1656
- when /\Acitext\z/io
1668
+ when /\Acitext\z/i
1657
1669
  :string
1658
1670
  else
1659
1671
  super
@@ -1876,9 +1888,48 @@ module Sequel
1876
1888
  super
1877
1889
  end
1878
1890
 
1879
- # 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.
1880
1919
  def explain(opts=OPTS)
1881
- 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
1882
1933
  end
1883
1934
 
1884
1935
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
@@ -2387,6 +2438,83 @@ module Sequel
2387
2438
  join_from_sql(:USING, sql)
2388
2439
  end
2389
2440
 
2441
+ # Handle column aliases containing data types, useful for selecting from functions
2442
+ # that return the record data type.
2443
+ def derived_column_list_sql_append(sql, column_aliases)
2444
+ c = false
2445
+ comma = ', '
2446
+ column_aliases.each do |a|
2447
+ sql << comma if c
2448
+ if a.is_a?(Array)
2449
+ raise Error, "column aliases specified as arrays must have only 2 elements, the first is alias name and the second is data type" unless a.length == 2
2450
+ a, type = a
2451
+ identifier_append(sql, a)
2452
+ sql << " " << db.cast_type_literal(type).to_s
2453
+ else
2454
+ identifier_append(sql, a)
2455
+ end
2456
+ c ||= true
2457
+ end
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
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
+
2390
2518
  # Add ON CONFLICT clause if it should be used
2391
2519
  def insert_conflict_sql(sql)
2392
2520
  if opts = @opts[:insert_conflict]
@@ -2430,6 +2558,9 @@ module Sequel
2430
2558
  # Return the primary key to use for RETURNING in an INSERT statement
2431
2559
  def insert_pk
2432
2560
  (f = opts[:from]) && !f.empty? && (t = f.first)
2561
+
2562
+ t = t.call(self) if t.is_a? Sequel::SQL::DelayedEvaluation
2563
+
2433
2564
  case t
2434
2565
  when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2435
2566
  if pk = db.primary_key(t)
@@ -109,6 +109,8 @@ module Sequel
109
109
  # :database :: database name (filename or ':memory:' or file: URI)
110
110
  # :readonly :: open database in read-only mode; useful for reading
111
111
  # static data that you do not want to modify
112
+ # :disable_dqs :: disable double quoted strings in DDL and DML statements
113
+ # (requires SQLite 3.29.0+ and sqlite3 gem version 1.4.3+).
112
114
  # :timeout :: how long to wait for the database to be available if it
113
115
  # is locked, given in milliseconds (default is 5000)
114
116
  # :setup_regexp_function :: enable use of Regexp objects with SQL
@@ -128,6 +130,8 @@ module Sequel
128
130
  opts[:database] = ':memory:' if blank_object?(opts[:database])
129
131
  sqlite3_opts = {}
130
132
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
133
+ # SEQUEL6: Make strict: true the default behavior
134
+ sqlite3_opts[:strict] = typecast_value_boolean(opts[:disable_dqs]) if opts.has_key?(:disable_dqs)
131
135
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
132
136
  db.busy_timeout(typecast_value_integer(opts.fetch(:timeout, 5000)))
133
137
 
@@ -62,8 +62,7 @@ module Sequel
62
62
  private
63
63
 
64
64
  def database_specific_error_class(exception, opts)
65
- case exception.message
66
- when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
65
+ if exception.error_code == 1205
67
66
  DatabaseLockTimeout
68
67
  else
69
68
  super
data/lib/sequel/core.rb CHANGED
@@ -164,6 +164,21 @@ module Sequel
164
164
  JSON::ParserError
165
165
  end
166
166
 
167
+ if RUBY_VERSION >= '3.3'
168
+ # Create a new module using the block, and set the temporary name
169
+ # on it using the given a containing module and name.
170
+ def set_temp_name(mod)
171
+ mod.set_temporary_name(yield)
172
+ mod
173
+ end
174
+ # :nocov:
175
+ else
176
+ def set_temp_name(mod)
177
+ mod
178
+ end
179
+ end
180
+ # :nocov:
181
+
167
182
  # Convert given object to json and return the result.
168
183
  # This can be overridden to use an alternative json implementation.
169
184
  def object_to_json(obj, *args, &block)
@@ -17,7 +17,7 @@ module Sequel
17
17
  # as the dataset class.
18
18
  def dataset_class=(c)
19
19
  unless @dataset_modules.empty?
20
- c = Class.new(c)
20
+ c = Sequel.set_temp_name(Class.new(c)){"Sequel::Dataset::_Subclass"}
21
21
  @dataset_modules.each{|m| c.send(:include, m)}
22
22
  end
23
23
  @dataset_class = c
@@ -61,10 +61,10 @@ module Sequel
61
61
  # # SELECT id, name FROM table WHERE active ORDER BY id
62
62
  def extend_datasets(mod=nil, &block)
63
63
  raise(Error, "must provide either mod or block, not both") if mod && block
64
- mod = Dataset::DatasetModule.new(&block) if block
64
+ mod = Sequel.set_temp_name(Dataset::DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"} if block
65
65
  if @dataset_modules.empty?
66
66
  @dataset_modules = [mod]
67
- @dataset_class = Class.new(@dataset_class)
67
+ @dataset_class = Sequel.set_temp_name(Class.new(@dataset_class)){"Sequel::Dataset::_Subclass"}
68
68
  else
69
69
  @dataset_modules << mod
70
70
  end
@@ -26,6 +26,11 @@ module Sequel
26
26
  :time=>Sequel::SQLTime, :boolean=>[TrueClass, FalseClass].freeze, :float=>Float, :decimal=>BigDecimal,
27
27
  :blob=>Sequel::SQL::Blob}.freeze
28
28
 
29
+ # :nocov:
30
+ URI_PARSER = defined?(::URI::RFC2396_PARSER) ? ::URI::RFC2396_PARSER : ::URI::DEFAULT_PARSER
31
+ # :nocov:
32
+ private_constant :URI_PARSER
33
+
29
34
  # Nested hook Proc; each new hook Proc just wraps the previous one.
30
35
  @initialize_hook = proc{|db| }
31
36
 
@@ -85,7 +90,7 @@ module Sequel
85
90
  def self.options_from_uri(uri)
86
91
  uri_options = uri_to_options(uri)
87
92
  uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
88
- uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
93
+ uri_options.to_a.each{|k,v| uri_options[k] = URI_PARSER.unescape(v) if v.is_a?(String)}
89
94
  uri_options
90
95
  end
91
96
  private_class_method :options_from_uri
@@ -111,6 +116,10 @@ module Sequel
111
116
  # :after_connect :: A callable object called after each new connection is made, with the
112
117
  # connection object (and server argument if the callable accepts 2 arguments),
113
118
  # useful for customizations that you want to apply to all connections.
119
+ # :compare_connections_by_identity :: Whether to use compare_by_identity on hashes that use
120
+ # connection objects as keys. Defaults to true. This should only
121
+ # be set to false to work around bugs in libraries or
122
+ # ruby implementations.
114
123
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
115
124
  # but before any connections are created.
116
125
  # :cache_schema :: Whether schema should be cached for this Database instance
@@ -160,7 +169,7 @@ module Sequel
160
169
  @schemas = {}
161
170
  @prepared_statements = {}
162
171
  @transactions = {}
163
- @transactions.compare_by_identity
172
+ @transactions.compare_by_identity if typecast_value_boolean(@opts.fetch(:compare_connections_by_identity, true))
164
173
  @symbol_literal_cache = {}
165
174
 
166
175
  @timezone = nil
@@ -241,9 +250,13 @@ module Sequel
241
250
  # extension does not have specific support for Database objects, an Error will be raised.
242
251
  # Returns self.
243
252
  def extension(*exts)
244
- Sequel.extension(*exts)
245
253
  exts.each do |ext|
246
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
254
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
255
+ Sequel.extension(ext)
256
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
257
+ end
258
+
259
+ if pr
247
260
  if Sequel.synchronize{@loaded_extensions.include?(ext) ? false : (@loaded_extensions << ext)}
248
261
  pr.call(self)
249
262
  end
@@ -170,7 +170,7 @@ module Sequel
170
170
  c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type]) unless c.has_key?(:ruby_default)
171
171
  if c[:primary_key] && !auto_increment_set
172
172
  # If adapter didn't set it, assume that integer primary keys are auto incrementing
173
- c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/io)
173
+ c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/i)
174
174
  end
175
175
  if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
176
176
  c[:max_length] = max_length
@@ -390,25 +390,25 @@ module Sequel
390
390
  # such as :integer or :string.
391
391
  def schema_column_type(db_type)
392
392
  case db_type
393
- when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/io
393
+ when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/i
394
394
  :string
395
- when /\A(int(eger)?|(big|small|tiny)int)/io
395
+ when /\A(int(eger)?|(big|small|tiny)int)/i
396
396
  :integer
397
- when /\Adate\z/io
397
+ when /\Adate\z/i
398
398
  :date
399
- when /\A((small)?datetime|timestamp(\(\d\))?( with(out)? time zone)?)\z/io
399
+ when /\A((small)?datetime(\(\d\))?|timestamp(\(\d\))?( with(out)? time zone)?)\z/i
400
400
  :datetime
401
- when /\Atime( with(out)? time zone)?\z/io
401
+ when /\Atime( with(out)? time zone)?\z/i
402
402
  :time
403
- when /\A(bool(ean)?)\z/io
403
+ when /\A(bool(ean)?)\z/i
404
404
  :boolean
405
- when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
405
+ when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/i
406
406
  :float
407
- when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/io
407
+ when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/i
408
408
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
409
- when /bytea|blob|image|(var)?binary/io
409
+ when /bytea|blob|image|(var)?binary/i
410
410
  :blob
411
- when /\Aenum/io
411
+ when /\Aenum/i
412
412
  :enum
413
413
  end
414
414
  end
@@ -430,6 +430,10 @@ module Sequel
430
430
  # add_unique_constraint(:name, name: :unique_name) # ADD CONSTRAINT unique_name UNIQUE (name)
431
431
  #
432
432
  # Supports the same :deferrable option as CreateTableGenerator#column.
433
+ #
434
+ # PostgreSQL specific options:
435
+ #
436
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
433
437
  def add_unique_constraint(columns, opts = OPTS)
434
438
  @operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
435
439
  nil
@@ -483,6 +487,10 @@ module Sequel
483
487
  #
484
488
  # add_primary_key(:id) # ADD COLUMN id serial PRIMARY KEY
485
489
  # add_primary_key([:artist_id, :name]) # ADD PRIMARY KEY (artist_id, name)
490
+ #
491
+ # PostgreSQL specific options:
492
+ #
493
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
486
494
  def add_primary_key(name, opts = OPTS)
487
495
  return add_composite_primary_key(name, opts) if name.is_a?(Array)
488
496
  opts = @db.serial_primary_key_options.merge(opts)
@@ -19,7 +19,7 @@ module Sequel
19
19
  def with_extend(*mods, &block)
20
20
  c = _clone(:freeze=>false)
21
21
  c.extend(*mods) unless mods.empty?
22
- c.extend(DatasetModule.new(&block)) if block
22
+ c.extend(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location.join(':')})"}) if block
23
23
  c.freeze
24
24
  end
25
25
 
@@ -20,7 +20,8 @@ module Sequel
20
20
  def self.prepared_statements_module(code, mods, meths=DEFAULT_PREPARED_STATEMENT_MODULE_METHODS, &block)
21
21
  code = PREPARED_STATEMENT_MODULE_CODE[code] || code
22
22
 
23
- Module.new do
23
+ Module.new do
24
+ Sequel.set_temp_name(self){"Sequel::Dataset::_PreparedStatementsModule(#{block.source_location.join(':') if block})"}
24
25
  Array(mods).each do |mod|
25
26
  include mod
26
27
  end
@@ -90,8 +91,14 @@ module Sequel
90
91
  @opts[:log_sql]
91
92
  end
92
93
 
93
- # The type of prepared statement, should be one of :select, :first,
94
- # :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.
95
102
  def prepared_type
96
103
  @opts[:prepared_type]
97
104
  end
@@ -140,7 +147,7 @@ module Sequel
140
147
  # Returns the SQL for the prepared statement, depending on
141
148
  # the type of the statement and the prepared_modify_values.
142
149
  def prepared_sql
143
- case prepared_type
150
+ case prepared_sql_type
144
151
  when :select, :all, :each
145
152
  # Most common scenario, so listed first.
146
153
  select_sql
@@ -181,34 +188,30 @@ module Sequel
181
188
 
182
189
  # Run the method based on the type of prepared statement.
183
190
  def run(&block)
184
- case prepared_type
191
+ case type = prepared_type
185
192
  when :select, :all
186
- all(&block)
193
+ with_sql_all(prepared_sql, &block)
187
194
  when :each
188
- each(&block)
189
- when :insert_select
190
- with_sql(prepared_sql).first
191
- when :first
192
- first
195
+ with_sql_each(prepared_sql, &block)
196
+ when :insert_select, :first
197
+ with_sql_first(prepared_sql)
193
198
  when :insert, :update, :delete
194
199
  if opts[:returning] && supports_returning?(prepared_type)
195
200
  returning_fetch_rows(prepared_sql)
196
- elsif prepared_type == :delete
197
- delete
201
+ elsif type == :delete
202
+ with_sql_delete(prepared_sql)
198
203
  else
199
- public_send(prepared_type, *prepared_modify_values)
204
+ force_prepared_sql.public_send(type, *prepared_modify_values)
200
205
  end
201
- when :insert_pk
202
- fetch_rows(prepared_sql){|r| return r.values.first}
206
+ when :insert_pk, :single_value
207
+ with_sql_single_value(prepared_sql)
203
208
  when Array
204
209
  # :nocov:
205
- case prepared_type[0]
210
+ case type[0]
206
211
  # :nocov:
207
212
  when :map, :as_hash, :to_hash, :to_hash_groups
208
- public_send(*prepared_type, &block)
213
+ force_prepared_sql.public_send(*type, &block)
209
214
  end
210
- when :single_value
211
- single_value
212
215
  else
213
216
  raise Error, "unsupported prepared statement type used: #{prepared_type.inspect}"
214
217
  end
@@ -216,6 +219,16 @@ module Sequel
216
219
 
217
220
  private
218
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
+
219
232
  # Returns the value of the prepared_args hash for the given key.
220
233
  def prepared_arg(k)
221
234
  @opts[:bind_vars][k]
@@ -293,11 +306,12 @@ module Sequel
293
306
  prepared_sql, frags = Sequel::Dataset::PlaceholderLiteralizer::Recorder.new.send(:prepared_sql_and_frags, self, prepared_args) do |pl, ds|
294
307
  ds = ds.clone(:recorder=>pl)
295
308
 
296
- case type
309
+ sql_type = prepared_sql_type || type
310
+ case sql_type
297
311
  when :first, :single_value
298
312
  ds.limit(1)
299
313
  when :update, :insert, :insert_select, :delete
300
- ds.with_sql(:"#{type}_sql", *values)
314
+ ds.with_sql(:"#{sql_type}_sql", *values)
301
315
  when :insert_pk
302
316
  ds.with_sql(:insert_sql, *values)
303
317
  else
@@ -343,13 +357,23 @@ module Sequel
343
357
  clone(:bind_vars=>bind_vars)
344
358
  end
345
359
 
346
- # For the given type (:select, :first, :insert, :insert_select, :update, :delete, or :single_value),
347
- # run the sql with the bind variables specified in the hash. +values+ is a hash passed to
348
- # 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)
349
369
  #
350
370
  # DB[:table].where(id: :$id).call(:first, id: 1)
351
371
  # # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
352
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
353
377
  def call(type, bind_variables=OPTS, *values, &block)
354
378
  to_prepared_statement(type, values, :extend=>bound_variable_modules).call(bind_variables, &block)
355
379
  end
@@ -370,6 +394,14 @@ module Sequel
370
394
  # # => {:id=>1, :name=>'Blah'}
371
395
  #
372
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
373
405
  def prepare(type, name, *values)
374
406
  ps = to_prepared_statement(type, values, :name=>name, :extend=>prepared_statement_modules, :no_delayed_evaluations=>true)
375
407
 
@@ -386,6 +418,19 @@ module Sequel
386
418
  ps
387
419
  end
388
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
+
389
434
  protected
390
435
 
391
436
  # Return a cloned copy of the current dataset extended with
@@ -204,7 +204,7 @@ module Sequel
204
204
  # If no related extension file exists or the extension does not have
205
205
  # specific support for Dataset objects, an error will be raised.
206
206
  def extension(*exts)
207
- Sequel.extension(*exts)
207
+ exts.each{|ext| Sequel.extension(ext) unless Sequel.synchronize{EXTENSIONS[ext]}}
208
208
  mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
209
209
  if mods.all?
210
210
  with_extend(*mods)
@@ -1238,9 +1238,9 @@ module Sequel
1238
1238
  # Note that like Object#extend, when multiple modules are provided
1239
1239
  # as arguments the subclass includes the modules in reverse order.
1240
1240
  def with_extend(*mods, &block)
1241
- c = Class.new(self.class)
1241
+ c = Sequel.set_temp_name(Class.new(self.class)){"Sequel::Dataset::_Subclass"}
1242
1242
  c.include(*mods) unless mods.empty?
1243
- c.include(DatasetModule.new(&block)) if block
1243
+ c.include(Sequel.set_temp_name(DatasetModule.new(&block)){"Sequel::Dataset::_DatasetModule(#{block.source_location[0,2].join(':')})"}) if block
1244
1244
  o = c.freeze.allocate
1245
1245
  o.instance_variable_set(:@db, @db)
1246
1246
  o.instance_variable_set(:@opts, @opts)
@@ -1359,9 +1359,13 @@ module Sequel
1359
1359
  unless TRUE_FREEZE
1360
1360
  # Load the extensions into the receiver, without checking if the receiver is frozen.
1361
1361
  def _extension!(exts)
1362
- Sequel.extension(*exts)
1363
1362
  exts.each do |ext|
1364
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
1363
+ unless pr = Sequel.synchronize{EXTENSIONS[ext]}
1364
+ Sequel.extension(ext)
1365
+ pr = Sequel.synchronize{EXTENSIONS[ext]}
1366
+ end
1367
+
1368
+ if pr
1365
1369
  pr.call(self)
1366
1370
  else
1367
1371
  raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")