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.
- checksums.yaml +4 -4
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -0
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +2 -2
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +8 -8
- data/lib/sequel/adapters/mysql2.rb +8 -1
- data/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/mssql.rb +4 -3
- data/lib/sequel/adapters/shared/mysql.rb +8 -4
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +140 -9
- data/lib/sequel/adapters/sqlite.rb +4 -0
- data/lib/sequel/adapters/trilogy.rb +1 -2
- data/lib/sequel/core.rb +15 -0
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +17 -4
- data/lib/sequel/database/query.rb +11 -11
- data/lib/sequel/database/schema_generator.rb +8 -0
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +70 -25
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +19 -9
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/migration.rb +23 -3
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +6 -1
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +16 -4
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/string_agg.rb +2 -2
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +28 -3
- data/lib/sequel/model/base.rb +67 -18
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +4 -1
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -5
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +263 -0
- data/lib/sequel/sql.rb +15 -6
- data/lib/sequel/version.rb +1 -1
- 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 =
|
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 :
|
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/
|
1666
|
+
when /\Ainterval\z/i
|
1655
1667
|
:interval
|
1656
|
-
when /\Acitext\z/
|
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
|
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
|
-
|
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
|
|
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
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -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] =
|
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
|
-
|
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/
|
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)/
|
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)/
|
395
|
+
when /\A(int(eger)?|(big|small|tiny)int)/i
|
396
396
|
:integer
|
397
|
-
when /\Adate\z/
|
397
|
+
when /\Adate\z/i
|
398
398
|
:date
|
399
|
-
when /\A((small)?datetime
|
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/
|
401
|
+
when /\Atime( with(out)? time zone)?\z/i
|
402
402
|
:time
|
403
|
-
when /\A(bool(ean)?)\z/
|
403
|
+
when /\A(bool(ean)?)\z/i
|
404
404
|
:boolean
|
405
|
-
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/
|
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/
|
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/
|
409
|
+
when /bytea|blob|image|(var)?binary/i
|
410
410
|
:blob
|
411
|
-
when /\Aenum/
|
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
|
94
|
-
#
|
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
|
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
|
-
|
193
|
+
with_sql_all(prepared_sql, &block)
|
187
194
|
when :each
|
188
|
-
|
189
|
-
when :insert_select
|
190
|
-
|
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
|
197
|
-
|
201
|
+
elsif type == :delete
|
202
|
+
with_sql_delete(prepared_sql)
|
198
203
|
else
|
199
|
-
public_send(
|
204
|
+
force_prepared_sql.public_send(type, *prepared_modify_values)
|
200
205
|
end
|
201
|
-
when :insert_pk
|
202
|
-
|
206
|
+
when :insert_pk, :single_value
|
207
|
+
with_sql_single_value(prepared_sql)
|
203
208
|
when Array
|
204
209
|
# :nocov:
|
205
|
-
case
|
210
|
+
case type[0]
|
206
211
|
# :nocov:
|
207
212
|
when :map, :as_hash, :to_hash, :to_hash_groups
|
208
|
-
public_send(*
|
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
|
-
|
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(:"#{
|
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
|
347
|
-
#
|
348
|
-
#
|
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
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -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(
|
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
|
-
|
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})")
|