sequel 5.92.0 → 5.94.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/access.rb +2 -2
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -1
- data/lib/sequel/adapters/jdbc.rb +21 -7
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/shared/db2.rb +8 -3
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +206 -16
- data/lib/sequel/adapters/shared/sqlite.rb +3 -3
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +1 -1
- data/lib/sequel/connection_pool/timed_queue.rb +1 -1
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/misc.rb +3 -3
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +24 -10
- data/lib/sequel/database/schema_methods.rb +60 -26
- data/lib/sequel/dataset/prepared_statements.rb +68 -24
- data/lib/sequel/dataset/query.rb +10 -2
- data/lib/sequel/dataset/sql.rb +8 -5
- data/lib/sequel/extensions/async_thread_pool.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +1 -3
- data/lib/sequel/extensions/eval_inspect.rb +1 -1
- data/lib/sequel/extensions/inflector.rb +2 -2
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/pg_auto_parameterize.rb +5 -0
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/provenance.rb +1 -3
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/plugins/constraint_validations.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +3 -3
- data/lib/sequel/plugins/unused_associations.rb +4 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- 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: 05c58f7009f56c4891e863d95ccd90eeb7acdc03f192979ff0d4a203fa1924d4
|
4
|
+
data.tar.gz: 957d465eebddd41b7de48475af26b04f4ae6fb387662257b97b9bdbc682eb197
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb72cd090c58b8246b4a679082f9426f9aecf2d4815dffa2d96fe3be5a15b3e41b9ae7dd17effee49bfab82ac54c11d5d7d6aa67ff2a8aa54040145f5c72a3b0
|
7
|
+
data.tar.gz: 031fcb089d79550e485be488ec6d4b0d84024e4f686ebe953a08189c5b9d6e490c2351246b4258d8b78e9efafd013db61738f19e40a04afbb9fb1ec1796e5e6a
|
@@ -281,7 +281,7 @@ module Sequel
|
|
281
281
|
fetch_ado_schema(:columns, [nil,nil,table_name.to_s,nil]) do |row|
|
282
282
|
rows << AdoSchema::Column.new(row)
|
283
283
|
end
|
284
|
-
rows.
|
284
|
+
rows.sort_by! { |a| a["ORDINAL_POSITION"] }
|
285
285
|
end
|
286
286
|
|
287
287
|
def ado_schema_foreign_keys(table_name)
|
@@ -289,7 +289,7 @@ module Sequel
|
|
289
289
|
fetch_ado_schema(:foreign_keys, [nil,nil,nil,nil,nil,table_name.to_s]) do |row|
|
290
290
|
rows << row
|
291
291
|
end
|
292
|
-
rows.
|
292
|
+
rows.sort_by! { |a| a["ORDINAL"] }
|
293
293
|
end
|
294
294
|
|
295
295
|
def fetch_ado_schema(type, criteria=[])
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -234,7 +234,7 @@ module Sequel
|
|
234
234
|
end
|
235
235
|
|
236
236
|
def disconnect_error?(e, opts)
|
237
|
-
super || (e.is_a?(::WIN32OLERuntimeError) && e.message
|
237
|
+
super || (e.is_a?(::WIN32OLERuntimeError) && e.message.include?('Communication link failure'))
|
238
238
|
end
|
239
239
|
|
240
240
|
def rollback_transaction(conn, opts=OPTS)
|
@@ -26,7 +26,7 @@ module Sequel
|
|
26
26
|
|
27
27
|
# Raise a disconnect error if the SQL state of the cause of the exception indicates so.
|
28
28
|
def disconnect_error?(exception, opts)
|
29
|
-
exception.message
|
29
|
+
exception.message.start_with?("Communications link failure") || super
|
30
30
|
end
|
31
31
|
|
32
32
|
# Get the last inserted id using LAST_INSERT_ID().
|
@@ -64,7 +64,7 @@ module Sequel
|
|
64
64
|
|
65
65
|
# Convert tinyint(1) type to boolean
|
66
66
|
def schema_column_type(db_type)
|
67
|
-
db_type
|
67
|
+
db_type.start_with?("tinyint(1)") ? :boolean : super
|
68
68
|
end
|
69
69
|
|
70
70
|
# Run the default connection setting SQL statements.
|
@@ -55,7 +55,7 @@ module Sequel
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def disconnect_error?(exception, opts)
|
58
|
-
super || exception.message
|
58
|
+
super || exception.message.start_with?("Closed Connection")
|
59
59
|
end
|
60
60
|
|
61
61
|
# Default the fetch size for statements to 100, similar to the oci8-based oracle adapter.
|
@@ -98,7 +98,7 @@ module Sequel
|
|
98
98
|
|
99
99
|
# Whether the given exception is due to a foreign key error.
|
100
100
|
def foreign_key_error?(exception)
|
101
|
-
exception.message
|
101
|
+
exception.message.include?('query does not return ResultSet')
|
102
102
|
end
|
103
103
|
|
104
104
|
# Use getLong instead of getInt for converting integers on SQLite, since SQLite does not enforce a limit of 2**32.
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -305,17 +305,31 @@ module Sequel
|
|
305
305
|
m = output_identifier_meth
|
306
306
|
schema, table = metadata_schema_and_table(table, opts)
|
307
307
|
foreign_keys = {}
|
308
|
+
|
308
309
|
metadata(:getImportedKeys, nil, schema, table) do |r|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
310
|
+
next unless fk_name = r[:fk_name]
|
311
|
+
|
312
|
+
key_seq = r[:key_seq]
|
313
|
+
columns = [key_seq, m.call(r[:fkcolumn_name])]
|
314
|
+
key = [key_seq, m.call(r[:pkcolumn_name])]
|
315
|
+
|
316
|
+
if fk = foreign_keys[fk_name]
|
317
|
+
fk[:columns] << columns
|
318
|
+
fk[:key] << key
|
319
|
+
else
|
320
|
+
foreign_keys[fk_name] = {
|
321
|
+
:name=>m.call(fk_name),
|
322
|
+
:columns=>[columns],
|
323
|
+
:table=>m.call(r[:pktable_name]),
|
324
|
+
:key=>[key]
|
325
|
+
}
|
314
326
|
end
|
315
327
|
end
|
328
|
+
|
329
|
+
fk_keys = [:columns, :key]
|
316
330
|
foreign_keys.values.each do |fk|
|
317
|
-
|
318
|
-
fk[k]
|
331
|
+
fk_keys.each do |k|
|
332
|
+
fk[k].sort!.map!{|_, v| v}
|
319
333
|
end
|
320
334
|
end
|
321
335
|
end
|
@@ -290,7 +290,7 @@ module Sequel
|
|
290
290
|
|
291
291
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
292
292
|
def schema_column_type(db_type)
|
293
|
-
convert_tinyint_to_bool && db_type
|
293
|
+
convert_tinyint_to_bool && db_type.start_with?("tinyint(1)") ? :boolean : super
|
294
294
|
end
|
295
295
|
end
|
296
296
|
|
@@ -221,13 +221,13 @@ module Sequel
|
|
221
221
|
super ||
|
222
222
|
((conn = opts[:conn]) && !conn.ping) ||
|
223
223
|
(e.is_a?(::Mysql2::Error) &&
|
224
|
-
(e.sql_state
|
224
|
+
((e.sql_state && e.sql_state.start_with?("08")) ||
|
225
225
|
MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)))
|
226
226
|
end
|
227
227
|
|
228
228
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
229
229
|
def schema_column_type(db_type)
|
230
|
-
convert_tinyint_to_bool && db_type
|
230
|
+
convert_tinyint_to_bool && db_type.start_with?("tinyint(1)") ? :boolean : super
|
231
231
|
end
|
232
232
|
end
|
233
233
|
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -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
|
@@ -108,7 +108,7 @@ module Sequel
|
|
108
108
|
from(name).first
|
109
109
|
true
|
110
110
|
rescue DatabaseError => e
|
111
|
-
if e.to_s
|
111
|
+
if e.to_s.include?('Operation not allowed for reason code "7" on table') && v == false
|
112
112
|
# table probably needs reorg
|
113
113
|
reorg(name)
|
114
114
|
v = true
|
@@ -175,8 +175,13 @@ module Sequel
|
|
175
175
|
# Supply columns with NOT NULL if they are part of a composite
|
176
176
|
# primary key or unique constraint
|
177
177
|
def column_list_sql(g)
|
178
|
-
ks =
|
179
|
-
g.constraints.each
|
178
|
+
ks = {}
|
179
|
+
g.constraints.each do |c|
|
180
|
+
case c[:type]
|
181
|
+
when :primary_key, :unique
|
182
|
+
c[:columns].each{|c| ks[c] = true}
|
183
|
+
end
|
184
|
+
end
|
180
185
|
g.columns.each{|c| c[:null] = false if ks.include?(c[:name]) }
|
181
186
|
super
|
182
187
|
end
|
@@ -705,7 +705,7 @@ module Sequel
|
|
705
705
|
|
706
706
|
# Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
|
707
707
|
def nolock
|
708
|
-
|
708
|
+
cached_lock_style_dataset(:_nolock_ds, :dirty)
|
709
709
|
end
|
710
710
|
|
711
711
|
# Uses OUTER APPLY to join the given table into the current dataset.
|
@@ -456,7 +456,7 @@ module Sequel
|
|
456
456
|
[pk]
|
457
457
|
elsif !(pkc = generator.constraints.select{|con| con[:type] == :primary_key}).empty?
|
458
458
|
pkc.first[:columns]
|
459
|
-
elsif !(pkc = generator.columns.select{|con| con[:primary_key]
|
459
|
+
elsif !(pkc = generator.columns.select{|con| con[:primary_key]}).empty?
|
460
460
|
pkc.map{|c| c[:name]}
|
461
461
|
end
|
462
462
|
else
|
@@ -787,7 +787,7 @@ module Sequel
|
|
787
787
|
|
788
788
|
# Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
|
789
789
|
def for_share
|
790
|
-
|
790
|
+
cached_lock_style_dataset(:_for_share_ds, :share)
|
791
791
|
end
|
792
792
|
|
793
793
|
# Adds full text filter
|
@@ -48,7 +48,7 @@ module Sequel
|
|
48
48
|
TYPE_TRANSLATOR_DATE = date.freeze
|
49
49
|
bytea = Object.new
|
50
50
|
def bytea.call(str)
|
51
|
-
str = if str
|
51
|
+
str = if str.start_with?('\\x')
|
52
52
|
# PostgreSQL 9.0+ bytea hex format
|
53
53
|
str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
|
54
54
|
else
|
@@ -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
|
@@ -141,6 +141,17 @@ module Sequel
|
|
141
141
|
@operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge!(opts)
|
142
142
|
end
|
143
143
|
|
144
|
+
# Alter an existing constraint. Options:
|
145
|
+
# :deferrable :: Modify deferrable setting for constraint (PostgreSQL 9.4+):
|
146
|
+
# true :: DEFERRABLE INITIALLY DEFERRED
|
147
|
+
# false :: NOT DEFERRABLE
|
148
|
+
# :immediate :: DEFERRABLE INITIALLY IMMEDIATE
|
149
|
+
# :enforced :: Set true to use ENFORCED, or false to use NOT ENFORCED (PostgreSQL 18+)
|
150
|
+
# :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
|
151
|
+
def alter_constraint(name, opts=OPTS)
|
152
|
+
@operations << {:op => :alter_constraint, :name => name}.merge!(opts)
|
153
|
+
end
|
154
|
+
|
144
155
|
# Validate the constraint with the given name, which should have
|
145
156
|
# been added previously with NOT VALID.
|
146
157
|
def validate_constraint(name)
|
@@ -335,7 +346,7 @@ module Sequel
|
|
335
346
|
hash = {}
|
336
347
|
_check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
|
337
348
|
constraint = m.call(row[:constraint])
|
338
|
-
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
349
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[], :validated=>row[:validated], :enforced=>row[:enforced]}
|
339
350
|
entry[:columns] << m.call(row[:column]) if row[:column]
|
340
351
|
end
|
341
352
|
|
@@ -601,6 +612,8 @@ module Sequel
|
|
601
612
|
:on_update=>fklod_map[row[:on_update]],
|
602
613
|
:on_delete=>fklod_map[row[:on_delete]],
|
603
614
|
:deferrable=>row[:deferrable],
|
615
|
+
:validated=>row[:validated],
|
616
|
+
:enforced=>row[:enforced],
|
604
617
|
:table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
|
605
618
|
}
|
606
619
|
|
@@ -898,11 +911,15 @@ module Sequel
|
|
898
911
|
|
899
912
|
# Dataset used to retrieve CHECK constraint information
|
900
913
|
def _check_constraints_ds
|
901
|
-
@_check_constraints_ds ||=
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
914
|
+
@_check_constraints_ds ||= begin
|
915
|
+
ds = metadata_dataset.
|
916
|
+
from{pg_constraint.as(:co)}.
|
917
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
918
|
+
where(:contype=>'c').
|
919
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
920
|
+
|
921
|
+
_add_validated_enforced_constraint_columns(ds)
|
922
|
+
end
|
906
923
|
end
|
907
924
|
|
908
925
|
# Dataset used to retrieve foreign keys referenced by a table
|
@@ -968,6 +985,28 @@ module Sequel
|
|
968
985
|
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
969
986
|
end
|
970
987
|
|
988
|
+
_add_validated_enforced_constraint_columns(ds)
|
989
|
+
end
|
990
|
+
|
991
|
+
def _add_validated_enforced_constraint_columns(ds)
|
992
|
+
validated_cond = if server_version >= 90100
|
993
|
+
Sequel[:convalidated]
|
994
|
+
# :nocov:
|
995
|
+
else
|
996
|
+
Sequel.cast(true, TrueClass)
|
997
|
+
# :nocov:
|
998
|
+
end
|
999
|
+
ds = ds.select_append(validated_cond.as(:validated))
|
1000
|
+
|
1001
|
+
enforced_cond = if server_version >= 180000
|
1002
|
+
Sequel[:conenforced]
|
1003
|
+
# :nocov:
|
1004
|
+
else
|
1005
|
+
Sequel.cast(true, TrueClass)
|
1006
|
+
# :nocov:
|
1007
|
+
end
|
1008
|
+
ds = ds.select_append(enforced_cond.as(:enforced))
|
1009
|
+
|
971
1010
|
ds
|
972
1011
|
end
|
973
1012
|
|
@@ -1140,6 +1179,31 @@ module Sequel
|
|
1140
1179
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
1141
1180
|
end
|
1142
1181
|
|
1182
|
+
def alter_table_alter_constraint_sql(table, op)
|
1183
|
+
sql = String.new
|
1184
|
+
sql << "ALTER CONSTRAINT #{quote_identifier(op[:name])}"
|
1185
|
+
|
1186
|
+
constraint_deferrable_sql_append(sql, op[:deferrable])
|
1187
|
+
|
1188
|
+
case op[:enforced]
|
1189
|
+
when nil
|
1190
|
+
when false
|
1191
|
+
sql << " NOT ENFORCED"
|
1192
|
+
else
|
1193
|
+
sql << " ENFORCED"
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
case op[:inherit]
|
1197
|
+
when nil
|
1198
|
+
when false
|
1199
|
+
sql << " NO INHERIT"
|
1200
|
+
else
|
1201
|
+
sql << " INHERIT"
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
sql
|
1205
|
+
end
|
1206
|
+
|
1143
1207
|
def alter_table_generator_class
|
1144
1208
|
Postgres::AlterTableGenerator
|
1145
1209
|
end
|
@@ -1233,9 +1297,9 @@ module Sequel
|
|
1233
1297
|
end
|
1234
1298
|
|
1235
1299
|
# PostgreSQL can't combine rename_column operations, and it can combine
|
1236
|
-
#
|
1300
|
+
# validate_constraint and alter_constraint operations.
|
1237
1301
|
def combinable_alter_table_op?(op)
|
1238
|
-
(super || op[:op] == :validate_constraint) && op[:op] != :rename_column
|
1302
|
+
(super || op[:op] == :validate_constraint || op[:op] == :alter_constraint) && op[:op] != :rename_column
|
1239
1303
|
end
|
1240
1304
|
|
1241
1305
|
VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
|
@@ -1269,7 +1333,7 @@ module Sequel
|
|
1269
1333
|
sqls
|
1270
1334
|
end
|
1271
1335
|
|
1272
|
-
# Handle
|
1336
|
+
# Handle PostgreSQL-specific constraint features.
|
1273
1337
|
def constraint_definition_sql(constraint)
|
1274
1338
|
case type = constraint[:type]
|
1275
1339
|
when :exclude
|
@@ -1293,6 +1357,9 @@ module Sequel
|
|
1293
1357
|
end
|
1294
1358
|
else # when :foreign_key, :check
|
1295
1359
|
sql = super
|
1360
|
+
if constraint[:not_enforced]
|
1361
|
+
sql << " NOT ENFORCED"
|
1362
|
+
end
|
1296
1363
|
if constraint[:not_valid]
|
1297
1364
|
sql << " NOT VALID"
|
1298
1365
|
end
|
@@ -1300,6 +1367,13 @@ module Sequel
|
|
1300
1367
|
end
|
1301
1368
|
end
|
1302
1369
|
|
1370
|
+
def column_definition_add_references_sql(sql, column)
|
1371
|
+
super
|
1372
|
+
if column[:not_enforced]
|
1373
|
+
sql << " NOT ENFORCED"
|
1374
|
+
end
|
1375
|
+
end
|
1376
|
+
|
1303
1377
|
def database_specific_error_class_from_sqlstate(sqlstate)
|
1304
1378
|
if sqlstate == '23P01'
|
1305
1379
|
ExclusionConstraintViolation
|
@@ -1369,7 +1443,8 @@ module Sequel
|
|
1369
1443
|
# SQL statement to create database function.
|
1370
1444
|
def create_function_sql(name, definition, opts=OPTS)
|
1371
1445
|
args = opts[:args]
|
1372
|
-
|
1446
|
+
in_out = %w'OUT INOUT'
|
1447
|
+
if (!opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 && in_out.include?(a[2].to_s)})
|
1373
1448
|
returns = opts[:returns] || 'void'
|
1374
1449
|
end
|
1375
1450
|
language = opts[:language] || 'SQL'
|
@@ -1888,14 +1963,66 @@ module Sequel
|
|
1888
1963
|
super
|
1889
1964
|
end
|
1890
1965
|
|
1891
|
-
# Return the results of an EXPLAIN query
|
1966
|
+
# Return the results of an EXPLAIN query. Boolean options:
|
1967
|
+
#
|
1968
|
+
# :analyze :: Use the ANALYZE option.
|
1969
|
+
# :buffers :: Use the BUFFERS option.
|
1970
|
+
# :costs :: Use the COSTS option.
|
1971
|
+
# :generic_plan :: Use the GENERIC_PLAN option.
|
1972
|
+
# :memory :: Use the MEMORY option.
|
1973
|
+
# :settings :: Use the SETTINGS option.
|
1974
|
+
# :summary :: Use the SUMMARY option.
|
1975
|
+
# :timing :: Use the TIMING option.
|
1976
|
+
# :verbose :: Use the VERBOSE option.
|
1977
|
+
# :wal :: Use the WAL option.
|
1978
|
+
#
|
1979
|
+
# Non boolean options:
|
1980
|
+
#
|
1981
|
+
# :format :: Use the FORMAT option to change the format of the
|
1982
|
+
# returned value. Values can be :text, :xml, :json,
|
1983
|
+
# or :yaml.
|
1984
|
+
# :serialize :: Use the SERIALIZE option to get timing on
|
1985
|
+
# serialization. Values can be :none, :text, or
|
1986
|
+
# :binary.
|
1987
|
+
#
|
1988
|
+
# See the PostgreSQL EXPLAIN documentation for an explanation of
|
1989
|
+
# what each option does.
|
1990
|
+
#
|
1991
|
+
# In most cases, the return value is a single string. However,
|
1992
|
+
# using the <tt>format: :json</tt> option can result in the return
|
1993
|
+
# value being an array containing a hash.
|
1892
1994
|
def explain(opts=OPTS)
|
1893
|
-
|
1995
|
+
rows = clone(:append_sql=>explain_sql_string_origin(opts)).map(:'QUERY PLAN')
|
1996
|
+
|
1997
|
+
if rows.length == 1
|
1998
|
+
rows[0]
|
1999
|
+
elsif rows.all?{|row| String === row}
|
2000
|
+
rows.join("\r\n")
|
2001
|
+
# :nocov:
|
2002
|
+
else
|
2003
|
+
# This branch is unreachable in tests, but it seems better to just return
|
2004
|
+
# all rows than throw in error if this case actually happens.
|
2005
|
+
rows
|
2006
|
+
# :nocov:
|
2007
|
+
end
|
2008
|
+
end
|
2009
|
+
|
2010
|
+
# Return a cloned dataset which will use FOR KEY SHARE to lock returned rows.
|
2011
|
+
# Supported on PostgreSQL 9.3+.
|
2012
|
+
def for_key_share
|
2013
|
+
cached_lock_style_dataset(:_for_key_share_ds, :key_share)
|
2014
|
+
end
|
2015
|
+
|
2016
|
+
# Return a cloned dataset which will use FOR NO KEY UPDATE to lock returned rows.
|
2017
|
+
# This is generally a better choice than using for_update on PostgreSQL, unless
|
2018
|
+
# you will be deleting the row or modifying a key column. Supported on PostgreSQL 9.3+.
|
2019
|
+
def for_no_key_update
|
2020
|
+
cached_lock_style_dataset(:_for_no_key_update_ds, :no_key_update)
|
1894
2021
|
end
|
1895
2022
|
|
1896
2023
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
1897
2024
|
def for_share
|
1898
|
-
|
2025
|
+
cached_lock_style_dataset(:_for_share_ds, :share)
|
1899
2026
|
end
|
1900
2027
|
|
1901
2028
|
# Run a full text search on PostgreSQL. By default, searching for the inclusion
|
@@ -2417,7 +2544,65 @@ module Sequel
|
|
2417
2544
|
c ||= true
|
2418
2545
|
end
|
2419
2546
|
end
|
2547
|
+
|
2548
|
+
EXPLAIN_BOOLEAN_OPTIONS = {}
|
2549
|
+
%w[analyze verbose costs settings generic_plan buffers wal timing summary memory].each do |str|
|
2550
|
+
EXPLAIN_BOOLEAN_OPTIONS[str.to_sym] = str.upcase.freeze
|
2551
|
+
end
|
2552
|
+
EXPLAIN_BOOLEAN_OPTIONS.freeze
|
2553
|
+
|
2554
|
+
EXPLAIN_NONBOOLEAN_OPTIONS = {
|
2555
|
+
:serialize => {:none=>"SERIALIZE NONE", :text=>"SERIALIZE TEXT", :binary=>"SERIALIZE BINARY"}.freeze,
|
2556
|
+
:format => {:text=>"FORMAT TEXT", :xml=>"FORMAT XML", :json=>"FORMAT JSON", :yaml=>"FORMAT YAML"}.freeze
|
2557
|
+
}.freeze
|
2420
2558
|
|
2559
|
+
# A mutable string used as the prefix when explaining a query.
|
2560
|
+
def explain_sql_string_origin(opts)
|
2561
|
+
origin = String.new
|
2562
|
+
origin << 'EXPLAIN '
|
2563
|
+
|
2564
|
+
# :nocov:
|
2565
|
+
if server_version < 90000
|
2566
|
+
if opts[:analyze]
|
2567
|
+
origin << 'ANALYZE '
|
2568
|
+
end
|
2569
|
+
|
2570
|
+
return origin
|
2571
|
+
end
|
2572
|
+
# :nocov:
|
2573
|
+
|
2574
|
+
comma = nil
|
2575
|
+
paren = "("
|
2576
|
+
|
2577
|
+
add_opt = lambda do |str, value|
|
2578
|
+
origin << paren if paren
|
2579
|
+
origin << comma if comma
|
2580
|
+
origin << str
|
2581
|
+
origin << " FALSE" unless value
|
2582
|
+
comma ||= ', '
|
2583
|
+
paren &&= nil
|
2584
|
+
end
|
2585
|
+
|
2586
|
+
EXPLAIN_BOOLEAN_OPTIONS.each do |key, str|
|
2587
|
+
unless (value = opts[key]).nil?
|
2588
|
+
add_opt.call(str, value)
|
2589
|
+
end
|
2590
|
+
end
|
2591
|
+
|
2592
|
+
EXPLAIN_NONBOOLEAN_OPTIONS.each do |key, e_opts|
|
2593
|
+
if value = opts[key]
|
2594
|
+
if str = e_opts[value]
|
2595
|
+
add_opt.call(str, true)
|
2596
|
+
else
|
2597
|
+
raise Sequel::Error, "unrecognized value for Dataset#explain #{key.inspect} option: #{value.inspect}"
|
2598
|
+
end
|
2599
|
+
end
|
2600
|
+
end
|
2601
|
+
|
2602
|
+
origin << ') ' unless paren
|
2603
|
+
origin
|
2604
|
+
end
|
2605
|
+
|
2421
2606
|
# Add ON CONFLICT clause if it should be used
|
2422
2607
|
def insert_conflict_sql(sql)
|
2423
2608
|
if opts = @opts[:insert_conflict]
|
@@ -2613,8 +2798,13 @@ module Sequel
|
|
2613
2798
|
# Use SKIP LOCKED if skipping locked rows.
|
2614
2799
|
def select_lock_sql(sql)
|
2615
2800
|
lock = @opts[:lock]
|
2616
|
-
|
2801
|
+
case lock
|
2802
|
+
when :share
|
2617
2803
|
sql << ' FOR SHARE'
|
2804
|
+
when :no_key_update
|
2805
|
+
sql << ' FOR NO KEY UPDATE'
|
2806
|
+
when :key_share
|
2807
|
+
sql << ' FOR KEY SHARE'
|
2618
2808
|
else
|
2619
2809
|
super
|
2620
2810
|
end
|
@@ -95,14 +95,14 @@ module Sequel
|
|
95
95
|
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
96
96
|
if opts[:only_autocreated]
|
97
97
|
# If specifically asked for only autocreated indexes, then return those an only those
|
98
|
-
next unless r[:name]
|
98
|
+
next unless r[:name].start_with?('sqlite_autoindex_')
|
99
99
|
elsif r.has_key?(:origin)
|
100
100
|
# If origin is set, then only exclude primary key indexes and partial indexes
|
101
101
|
next if r[:origin] == 'pk'
|
102
102
|
next if r[:partial].to_i == 1
|
103
103
|
else
|
104
104
|
# When :origin key not present, assume any autoindex could be a primary key one and exclude it
|
105
|
-
next if r[:name]
|
105
|
+
next if r[:name].start_with?('sqlite_autoindex_')
|
106
106
|
end
|
107
107
|
|
108
108
|
indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
|
@@ -302,7 +302,7 @@ module Sequel
|
|
302
302
|
|
303
303
|
# A name to use for the backup table
|
304
304
|
def backup_table_name(table, opts=OPTS)
|
305
|
-
table = table.
|
305
|
+
table = table.delete('`')
|
306
306
|
(opts[:times]||1000).times do |i|
|
307
307
|
table_name = "#{table}_backup#{i}"
|
308
308
|
return table_name unless table_exists?(table_name)
|
@@ -236,7 +236,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
|
|
236
236
|
def preconnect(concurrent = false)
|
237
237
|
if concurrent
|
238
238
|
if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
|
239
|
-
|
239
|
+
Array.new(times){Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
|
240
240
|
end
|
241
241
|
else
|
242
242
|
while conn = try_make_new
|