sequel 3.33.0 → 3.34.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.
- data/CHANGELOG +140 -0
- data/Rakefile +7 -0
- data/bin/sequel +22 -2
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/mass_assignment.rdoc +3 -1
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +23 -3
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/schema_modification.rdoc +18 -2
- data/doc/virtual_rows.rdoc +49 -0
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/ibmdb.rb +9 -4
- data/lib/sequel/adapters/jdbc.rb +9 -4
- data/lib/sequel/adapters/jdbc/h2.rb +8 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
- data/lib/sequel/adapters/mock.rb +24 -3
- data/lib/sequel/adapters/mysql.rb +29 -50
- data/lib/sequel/adapters/mysql2.rb +13 -28
- data/lib/sequel/adapters/oracle.rb +8 -2
- data/lib/sequel/adapters/postgres.rb +115 -20
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -3
- data/lib/sequel/adapters/shared/mysql.rb +59 -11
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
- data/lib/sequel/adapters/shared/oracle.rb +1 -1
- data/lib/sequel/adapters/shared/postgres.rb +127 -30
- data/lib/sequel/adapters/shared/sqlite.rb +55 -38
- data/lib/sequel/adapters/sqlite.rb +9 -3
- data/lib/sequel/adapters/swift.rb +2 -2
- data/lib/sequel/adapters/swift/mysql.rb +0 -5
- data/lib/sequel/adapters/swift/postgres.rb +10 -0
- data/lib/sequel/ast_transformer.rb +4 -0
- data/lib/sequel/connection_pool.rb +8 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +14 -0
- data/lib/sequel/core.rb +24 -3
- data/lib/sequel/database/connecting.rb +24 -14
- data/lib/sequel/database/dataset_defaults.rb +1 -0
- data/lib/sequel/database/misc.rb +16 -25
- data/lib/sequel/database/query.rb +20 -2
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +120 -23
- data/lib/sequel/dataset/actions.rb +91 -18
- data/lib/sequel/dataset/features.rb +5 -0
- data/lib/sequel/dataset/prepared_statements.rb +6 -2
- data/lib/sequel/dataset/sql.rb +68 -51
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
- data/lib/sequel/extensions/migration.rb +4 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +227 -31
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model.rb +11 -2
- data/lib/sequel/model/associations.rb +35 -7
- data/lib/sequel/model/base.rb +159 -36
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +11 -3
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +4 -3
- data/lib/sequel/plugins/prepared_statements.rb +3 -1
- data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/sql.rb +417 -7
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +12 -15
- data/spec/adapters/mysql_spec.rb +81 -23
- data/spec/adapters/postgres_spec.rb +444 -77
- data/spec/adapters/spec_helper.rb +2 -0
- data/spec/adapters/sqlite_spec.rb +8 -8
- data/spec/core/connection_pool_spec.rb +85 -0
- data/spec/core/database_spec.rb +29 -5
- data/spec/core/dataset_spec.rb +171 -3
- data/spec/core/expression_filters_spec.rb +364 -0
- data/spec/core/mock_adapter_spec.rb +17 -3
- data/spec/core/schema_spec.rb +133 -0
- data/spec/extensions/association_dependencies_spec.rb +13 -13
- data/spec/extensions/caching_spec.rb +26 -3
- data/spec/extensions/class_table_inheritance_spec.rb +2 -2
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +5 -2
- data/spec/extensions/identity_map_spec.rb +17 -0
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +2 -2
- data/spec/extensions/list_spec.rb +4 -4
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +6 -2
- data/spec/extensions/nested_attributes_spec.rb +20 -0
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +2 -2
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_spec.rb +4 -0
- data/spec/extensions/pretty_table_spec.rb +6 -0
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +231 -11
- data/spec/extensions/schema_spec.rb +14 -2
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/sharding_spec.rb +6 -6
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +2 -1
- data/spec/extensions/sql_expr_spec.rb +28 -19
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +9 -1
- data/spec/integration/associations_test.rb +6 -6
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +89 -26
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +3 -3
- data/spec/integration/plugin_test.rb +85 -22
- data/spec/integration/prepared_statement_test.rb +28 -8
- data/spec/integration/schema_test.rb +78 -7
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +4 -6
- data/spec/integration/type_test.rb +2 -2
- data/spec/model/associations_spec.rb +94 -8
- data/spec/model/base_spec.rb +4 -4
- data/spec/model/hooks_spec.rb +2 -2
- data/spec/model/model_spec.rb +19 -7
- data/spec/model/record_spec.rb +135 -58
- data/spec/model/spec_helper.rb +1 -0
- metadata +35 -7
|
@@ -50,6 +50,7 @@ module Sequel
|
|
|
50
50
|
end
|
|
51
51
|
i = 0
|
|
52
52
|
_execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
|
|
53
|
+
opts = opts.merge(:log_sql=>" (#{sql})") if ps.log_sql
|
|
53
54
|
_execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
|
|
54
55
|
end
|
|
55
56
|
end
|
|
@@ -94,6 +95,11 @@ module Sequel
|
|
|
94
95
|
def execute_dui(sql, opts={}, &block)
|
|
95
96
|
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
96
97
|
end
|
|
98
|
+
|
|
99
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
|
100
|
+
def execute_insert(sql, opts={}, &block)
|
|
101
|
+
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
102
|
+
end
|
|
97
103
|
end
|
|
98
104
|
|
|
99
105
|
# Methods for MySQL stored procedures using the native driver.
|
|
@@ -179,7 +179,7 @@ module Sequel
|
|
|
179
179
|
include EmulateOffsetWithRowNumber
|
|
180
180
|
|
|
181
181
|
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with select distinct columns from join where group having compounds order lock')
|
|
182
|
-
ROW_NUMBER_EXPRESSION = 'ROWNUM'.
|
|
182
|
+
ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
|
|
183
183
|
SPACE = Dataset::SPACE
|
|
184
184
|
APOS = Dataset::APOS
|
|
185
185
|
APOS_RE = Dataset::APOS_RE
|
|
@@ -57,7 +57,7 @@ module Sequel
|
|
|
57
57
|
attr_writer :db
|
|
58
58
|
|
|
59
59
|
SELECT_CURRVAL = "SELECT currval('%s')".freeze
|
|
60
|
-
|
|
60
|
+
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
61
61
|
SELECT '"' || name.nspname || '".' || CASE
|
|
62
62
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
|
63
63
|
substr(split_part(def.adsrc, '''', 2),
|
|
@@ -71,11 +71,10 @@ module Sequel
|
|
|
71
71
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
|
72
72
|
WHERE cons.contype = 'p'
|
|
73
73
|
AND def.adsrc ~* 'nextval'
|
|
74
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
75
|
-
AND t.relname = '#{table}'
|
|
76
74
|
end_sql
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
76
|
+
|
|
77
|
+
SELECT_PK_SQL = (<<-end_sql
|
|
79
78
|
SELECT pg_attribute.attname
|
|
80
79
|
FROM pg_class, pg_attribute, pg_index, pg_namespace
|
|
81
80
|
WHERE pg_class.oid = pg_attribute.attrelid
|
|
@@ -83,11 +82,10 @@ module Sequel
|
|
|
83
82
|
AND pg_class.oid = pg_index.indrelid
|
|
84
83
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
|
85
84
|
AND pg_index.indisprimary = 't'
|
|
86
|
-
#{"AND pg_namespace.nspname = '#{schema}'" if schema}
|
|
87
|
-
AND pg_class.relname = '#{table}'
|
|
88
85
|
end_sql
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
87
|
+
|
|
88
|
+
SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
|
|
91
89
|
SELECT '"' || name.nspname || '".' || seq.relname || ''
|
|
92
90
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
|
93
91
|
pg_namespace name, pg_constraint cons
|
|
@@ -99,10 +97,8 @@ module Sequel
|
|
|
99
97
|
AND attr.attrelid = cons.conrelid
|
|
100
98
|
AND attr.attnum = cons.conkey[1]
|
|
101
99
|
AND cons.contype = 'p'
|
|
102
|
-
#{"AND name.nspname = '#{schema}'" if schema}
|
|
103
|
-
AND seq.relname = '#{table}'
|
|
104
100
|
end_sql
|
|
105
|
-
|
|
101
|
+
).strip.gsub(/\s+/, ' ').freeze
|
|
106
102
|
|
|
107
103
|
# Depth of the current transaction on this connection, used
|
|
108
104
|
# to implement multi-level transactions with savepoints.
|
|
@@ -132,23 +128,30 @@ module Sequel
|
|
|
132
128
|
end
|
|
133
129
|
end
|
|
134
130
|
|
|
135
|
-
# Get the primary key for the given table.
|
|
131
|
+
# Get the primary key for the given table and schema. Both
|
|
132
|
+
# should be provided as literal SQL strings, with schema
|
|
133
|
+
# optionally nil.
|
|
136
134
|
def primary_key(schema, table)
|
|
137
|
-
sql =
|
|
135
|
+
sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{table}"
|
|
136
|
+
sql << "AND pg_namespace.nspname = #{schema}" if schema
|
|
138
137
|
execute(sql) do |r|
|
|
139
138
|
return single_value(r)
|
|
140
139
|
end
|
|
141
140
|
end
|
|
142
141
|
|
|
143
|
-
# Get the primary key and sequence for the given table.
|
|
142
|
+
# Get the primary key and sequence for the given table and schema.
|
|
143
|
+
# Both should be provided as literal SQL strings, with schema
|
|
144
|
+
# optionally nil.
|
|
144
145
|
def sequence(schema, table)
|
|
145
|
-
sql =
|
|
146
|
+
sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
|
|
147
|
+
sql << " AND name.nspname = #{schema}" if schema
|
|
146
148
|
execute(sql) do |r|
|
|
147
149
|
seq = single_value(r)
|
|
148
150
|
return seq if seq
|
|
149
151
|
end
|
|
150
152
|
|
|
151
|
-
sql =
|
|
153
|
+
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
154
|
+
sql << " AND name.nspname = #{schema}" if schema
|
|
152
155
|
execute(sql) do |r|
|
|
153
156
|
return single_value(r)
|
|
154
157
|
end
|
|
@@ -161,6 +164,7 @@ module Sequel
|
|
|
161
164
|
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
162
165
|
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
163
166
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
|
167
|
+
FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'.freeze=>:no_action, 'r'.freeze=>:restrict, 'c'.freeze=>:cascade, 'n'.freeze=>:set_null, 'd'.freeze=>:set_default}.freeze
|
|
164
168
|
|
|
165
169
|
# Commit an existing prepared transaction with the given transaction
|
|
166
170
|
# identifier string.
|
|
@@ -266,6 +270,64 @@ module Sequel
|
|
|
266
270
|
self << drop_trigger_sql(table, name, opts)
|
|
267
271
|
end
|
|
268
272
|
|
|
273
|
+
# Return full foreign key information using the pg system tables, including
|
|
274
|
+
# :name, :on_delete, :on_update, and :deferrable entries in the hashes.
|
|
275
|
+
def foreign_key_list(table, opts={})
|
|
276
|
+
m = output_identifier_meth
|
|
277
|
+
im = input_identifier_meth
|
|
278
|
+
schema, table = schema_and_table(table)
|
|
279
|
+
range = 0...32
|
|
280
|
+
|
|
281
|
+
base_ds = metadata_dataset.
|
|
282
|
+
where(:cl__relkind=>'r', :co__contype=>'f', :cl__relname=>im.call(table)).
|
|
283
|
+
from(:pg_constraint___co).
|
|
284
|
+
join(:pg_class___cl, :oid=>:conrelid)
|
|
285
|
+
|
|
286
|
+
# We split the parsing into two separate queries, which are merged manually later.
|
|
287
|
+
# This is because PostgreSQL stores both the referencing and referenced columns in
|
|
288
|
+
# arrays, and I don't know a simple way to not create a cross product, as PostgreSQL
|
|
289
|
+
# doesn't appear to have a function that takes an array and element and gives you
|
|
290
|
+
# the index of that element in the array.
|
|
291
|
+
|
|
292
|
+
ds = base_ds.
|
|
293
|
+
join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
|
|
294
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
|
|
295
|
+
select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
|
|
296
|
+
SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
|
|
297
|
+
|
|
298
|
+
ref_ds = base_ds.
|
|
299
|
+
join(:pg_class___cl2, :oid=>:co__confrelid).
|
|
300
|
+
join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
|
|
301
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att2__attnum)).
|
|
302
|
+
select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
|
|
303
|
+
|
|
304
|
+
# If a schema is given, we only search in that schema, and the returned :table
|
|
305
|
+
# entry is schema qualified as well.
|
|
306
|
+
if schema
|
|
307
|
+
ds.join!(:pg_namespace___nsp, :oid=>:cl__relnamespace).
|
|
308
|
+
where(:nsp___nspname=>im.call(schema))
|
|
309
|
+
ref_ds.join!(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
|
|
310
|
+
select_more(:nsp2__nspname___schema)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
h = {}
|
|
314
|
+
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
315
|
+
ds.each do |row|
|
|
316
|
+
if r = h[row[:name]]
|
|
317
|
+
r[:columns] << m.call(row[:column])
|
|
318
|
+
else
|
|
319
|
+
h[row[:name]] = {:name=>m.call(row[:name]), :columns=>[m.call(row[:column])], :on_update=>fklod_map[row[:on_update]], :on_delete=>fklod_map[row[:on_delete]], :deferrable=>row[:deferrable]}
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
ref_ds.each do |row|
|
|
323
|
+
r = h[row[:name]]
|
|
324
|
+
r[:table] ||= m.call(schema ? SQL::QualifiedIdentifier.new(row[:schema], row[:table]) : row[:table])
|
|
325
|
+
r[:key] ||= []
|
|
326
|
+
r[:key] << m.call(row[:refcolumn])
|
|
327
|
+
end
|
|
328
|
+
h.values
|
|
329
|
+
end
|
|
330
|
+
|
|
269
331
|
# Use the pg_* system tables to determine indexes on a table
|
|
270
332
|
def indexes(table, opts={})
|
|
271
333
|
m = output_identifier_meth
|
|
@@ -279,7 +341,7 @@ module Sequel
|
|
|
279
341
|
join(:pg_class___indc, :oid=>:indexrelid).
|
|
280
342
|
join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
|
|
281
343
|
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
|
|
282
|
-
order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}
|
|
344
|
+
order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
|
|
283
345
|
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
|
284
346
|
|
|
285
347
|
ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
|
|
@@ -314,9 +376,9 @@ module Sequel
|
|
|
314
376
|
quoted_table = quote_schema_table(table)
|
|
315
377
|
return @primary_keys[quoted_table] if @primary_keys.include?(quoted_table)
|
|
316
378
|
@primary_keys[quoted_table] = if conn = opts[:conn]
|
|
317
|
-
conn.primary_key(*
|
|
379
|
+
conn.primary_key(*schema_and_table_quoted_strings(table))
|
|
318
380
|
else
|
|
319
|
-
synchronize(opts[:server]){|con| con.primary_key(*
|
|
381
|
+
synchronize(opts[:server]){|con| con.primary_key(*schema_and_table_quoted_strings(table))}
|
|
320
382
|
end
|
|
321
383
|
end
|
|
322
384
|
|
|
@@ -325,9 +387,9 @@ module Sequel
|
|
|
325
387
|
quoted_table = quote_schema_table(table)
|
|
326
388
|
return @primary_key_sequences[quoted_table] if @primary_key_sequences.include?(quoted_table)
|
|
327
389
|
@primary_key_sequences[quoted_table] = if conn = opts[:conn]
|
|
328
|
-
conn.sequence(*
|
|
390
|
+
conn.sequence(*schema_and_table_quoted_strings(table))
|
|
329
391
|
else
|
|
330
|
-
synchronize(opts[:server]){|con| con.sequence(*
|
|
392
|
+
synchronize(opts[:server]){|con| con.sequence(*schema_and_table_quoted_strings(table))}
|
|
331
393
|
end
|
|
332
394
|
end
|
|
333
395
|
|
|
@@ -337,7 +399,7 @@ module Sequel
|
|
|
337
399
|
pk = SQL::Identifier.new(primary_key(table))
|
|
338
400
|
return unless seq = primary_key_sequence(table)
|
|
339
401
|
db = self
|
|
340
|
-
seq_ds = db.from(seq
|
|
402
|
+
seq_ds = db.from(LiteralString.new(seq))
|
|
341
403
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
|
342
404
|
end
|
|
343
405
|
|
|
@@ -366,6 +428,7 @@ module Sequel
|
|
|
366
428
|
0
|
|
367
429
|
end
|
|
368
430
|
end
|
|
431
|
+
warn 'Sequel support for PostgreSQL <8.2 is deprecated and will be removed in 3.35.0' if @server_version < 80200
|
|
369
432
|
@server_version
|
|
370
433
|
end
|
|
371
434
|
|
|
@@ -381,6 +444,11 @@ module Sequel
|
|
|
381
444
|
server_version >= 90100
|
|
382
445
|
end
|
|
383
446
|
|
|
447
|
+
# PostgreSQL supports DROP TABLE IF EXISTS on 8.2+
|
|
448
|
+
def supports_drop_table_if_exists?
|
|
449
|
+
server_version >= 80200
|
|
450
|
+
end
|
|
451
|
+
|
|
384
452
|
# PostgreSQL supports savepoints
|
|
385
453
|
def supports_savepoints?
|
|
386
454
|
true
|
|
@@ -402,6 +470,13 @@ module Sequel
|
|
|
402
470
|
pg_class_relname('r', opts, &block)
|
|
403
471
|
end
|
|
404
472
|
|
|
473
|
+
# Check whether the given type name string/symbol (e.g. :hstore) is supported by
|
|
474
|
+
# the database.
|
|
475
|
+
def type_supported?(type)
|
|
476
|
+
@supported_types ||= {}
|
|
477
|
+
@supported_types.fetch(type){@supported_types[type] = (from(:pg_type).filter(:typtype=>'b', :typname=>type.to_s).count > 0)}
|
|
478
|
+
end
|
|
479
|
+
|
|
405
480
|
# Array of symbols specifying view names in the current database.
|
|
406
481
|
#
|
|
407
482
|
# Options:
|
|
@@ -521,7 +596,7 @@ module Sequel
|
|
|
521
596
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
|
522
597
|
case index_type
|
|
523
598
|
when :full_text
|
|
524
|
-
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
599
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
525
600
|
index_type = :gin
|
|
526
601
|
when :spatial
|
|
527
602
|
index_type = :gist
|
|
@@ -591,6 +666,13 @@ module Sequel
|
|
|
591
666
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
592
667
|
end
|
|
593
668
|
|
|
669
|
+
# Split the table into a schema and table, and return the values as quoted strings for usage
|
|
670
|
+
# in querying the system tables.
|
|
671
|
+
def schema_and_table_quoted_strings(table)
|
|
672
|
+
schema, table = schema_and_table(table)
|
|
673
|
+
[(literal(schema) if schema), literal(table)]
|
|
674
|
+
end
|
|
675
|
+
|
|
594
676
|
# PostgreSQL's autoincrementing primary keys are of type integer or bigint
|
|
595
677
|
# using a nextval function call as a default.
|
|
596
678
|
def schema_autoincrementing_primary_key?(schema)
|
|
@@ -677,6 +759,7 @@ module Sequel
|
|
|
677
759
|
BOOL_TRUE = 'true'.freeze
|
|
678
760
|
COMMA_SEPARATOR = ', '.freeze
|
|
679
761
|
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where')
|
|
762
|
+
DELETE_CLAUSE_METHODS_82 = Dataset.clause_methods(:delete, %w'delete from using where returning')
|
|
680
763
|
DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
|
|
681
764
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
682
765
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
@@ -698,6 +781,7 @@ module Sequel
|
|
|
698
781
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
699
782
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
700
783
|
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where')
|
|
784
|
+
UPDATE_CLAUSE_METHODS_82 = Dataset.clause_methods(:update, %w'update table set from where returning')
|
|
701
785
|
UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
|
|
702
786
|
SPACE = Dataset::SPACE
|
|
703
787
|
FROM = Dataset::FROM
|
|
@@ -717,7 +801,7 @@ module Sequel
|
|
|
717
801
|
# Shared methods for prepared statements when used with PostgreSQL databases.
|
|
718
802
|
module PreparedStatementMethods
|
|
719
803
|
# Override insert action to use RETURNING if the server supports it.
|
|
720
|
-
def run
|
|
804
|
+
def run
|
|
721
805
|
if @prepared_type == :insert && supports_insert_select?
|
|
722
806
|
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
723
807
|
else
|
|
@@ -743,7 +827,7 @@ module Sequel
|
|
|
743
827
|
mod.def_mutation_method(:disable_insert_returning)
|
|
744
828
|
end
|
|
745
829
|
|
|
746
|
-
# Return the results of an ANALYZE query as a string
|
|
830
|
+
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
747
831
|
def analyze
|
|
748
832
|
explain(:analyze=>true)
|
|
749
833
|
end
|
|
@@ -767,6 +851,7 @@ module Sequel
|
|
|
767
851
|
|
|
768
852
|
# Disable the use of INSERT RETURNING, even if the server supports it
|
|
769
853
|
def disable_insert_returning
|
|
854
|
+
warn("disable_insert_returning is deprecated and will be removed in Sequel 3.35.0")
|
|
770
855
|
clone(:disable_insert_returning=>true)
|
|
771
856
|
end
|
|
772
857
|
|
|
@@ -785,11 +870,11 @@ module Sequel
|
|
|
785
870
|
def full_text_search(cols, terms, opts = {})
|
|
786
871
|
lang = opts[:language] || 'simple'
|
|
787
872
|
terms = terms.join(' | ') if terms.is_a?(Array)
|
|
788
|
-
filter("to_tsvector(
|
|
873
|
+
filter("to_tsvector(?::regconfig, ?) @@ to_tsquery(?::regconfig, ?)", lang, full_text_string_join(cols), lang, terms)
|
|
789
874
|
end
|
|
790
875
|
|
|
791
876
|
# Insert given values into the database.
|
|
792
|
-
def insert(*values
|
|
877
|
+
def insert(*values)
|
|
793
878
|
if @opts[:returning]
|
|
794
879
|
super
|
|
795
880
|
elsif !@opts[:sql] && supports_insert_select?
|
|
@@ -904,7 +989,13 @@ module Sequel
|
|
|
904
989
|
|
|
905
990
|
# PostgreSQL allows deleting from joined datasets
|
|
906
991
|
def delete_clause_methods
|
|
907
|
-
server_version >= 90100
|
|
992
|
+
if (sv = server_version) >= 90100
|
|
993
|
+
DELETE_CLAUSE_METHODS_91
|
|
994
|
+
elsif sv >= 80200
|
|
995
|
+
DELETE_CLAUSE_METHODS_82
|
|
996
|
+
else
|
|
997
|
+
DELETE_CLAUSE_METHODS
|
|
998
|
+
end
|
|
908
999
|
end
|
|
909
1000
|
|
|
910
1001
|
# Only include the primary table in the main delete clause
|
|
@@ -1035,7 +1126,13 @@ module Sequel
|
|
|
1035
1126
|
|
|
1036
1127
|
# PostgreSQL splits the main table from the joined tables
|
|
1037
1128
|
def update_clause_methods
|
|
1038
|
-
server_version >= 90100
|
|
1129
|
+
if (sv = server_version) >= 90100
|
|
1130
|
+
UPDATE_CLAUSE_METHODS_91
|
|
1131
|
+
elsif sv >= 80200
|
|
1132
|
+
UPDATE_CLAUSE_METHODS_82
|
|
1133
|
+
else
|
|
1134
|
+
UPDATE_CLAUSE_METHODS
|
|
1135
|
+
end
|
|
1039
1136
|
end
|
|
1040
1137
|
|
|
1041
1138
|
# Use FROM to specify additional tables in an update query
|
|
@@ -59,22 +59,33 @@ module Sequel
|
|
|
59
59
|
pragma_set(:foreign_keys, !!value ? 'on' : 'off') if sqlite_version >= 30619
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
|
|
63
|
+
# including information for the :on_update and :on_delete entries.
|
|
64
|
+
def foreign_key_list(table, opts={})
|
|
65
|
+
m = output_identifier_meth
|
|
66
|
+
h = {}
|
|
67
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
68
|
+
if r = h[row[:id]]
|
|
69
|
+
r[:columns] << m.call(row[:from])
|
|
70
|
+
r[:key] << m.call(row[:to]) if r[:key]
|
|
71
|
+
else
|
|
72
|
+
h[row[:id]] = {:columns=>[m.call(row[:from])], :table=>m.call(row[:table]), :key=>([m.call(row[:to])] if row[:to]), :on_update=>on_delete_sql_to_sym(row[:on_update]), :on_delete=>on_delete_sql_to_sym(row[:on_delete])}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
h.values
|
|
76
|
+
end
|
|
77
|
+
|
|
62
78
|
# Use the index_list and index_info PRAGMAs to determine the indexes on the table.
|
|
63
79
|
def indexes(table, opts={})
|
|
64
80
|
m = output_identifier_meth
|
|
65
81
|
im = input_identifier_meth
|
|
66
82
|
indexes = {}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
nil
|
|
74
|
-
else
|
|
75
|
-
indexes.each do |k, v|
|
|
76
|
-
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
|
77
|
-
end
|
|
83
|
+
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
|
84
|
+
next if r[:name] =~ PRIMARY_KEY_INDEX_RE
|
|
85
|
+
indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
|
|
86
|
+
end
|
|
87
|
+
indexes.each do |k, v|
|
|
88
|
+
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
|
|
78
89
|
end
|
|
79
90
|
indexes
|
|
80
91
|
end
|
|
@@ -226,7 +237,7 @@ module Sequel
|
|
|
226
237
|
when :primary_key
|
|
227
238
|
duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
|
|
228
239
|
when :foreign_key
|
|
229
|
-
duplicate_table(table
|
|
240
|
+
duplicate_table(table, :no_foreign_keys=>true)
|
|
230
241
|
when :unique
|
|
231
242
|
duplicate_table(table)
|
|
232
243
|
else
|
|
@@ -283,20 +294,6 @@ module Sequel
|
|
|
283
294
|
nono= Array(opts[:except]).compact.map{|n| n.to_s}
|
|
284
295
|
cols.reject!{|c| nono.include? c[:name] }
|
|
285
296
|
end
|
|
286
|
-
|
|
287
|
-
begin
|
|
288
|
-
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
|
|
289
|
-
c = cols.find {|co| co[:name] == row[:from] } or next
|
|
290
|
-
c[:table] = row[:table]
|
|
291
|
-
c[:key] = row[:to]
|
|
292
|
-
c[:on_update] = on_delete_sql_to_sym(row[:on_update])
|
|
293
|
-
c[:on_delete] = on_delete_sql_to_sym(row[:on_delete])
|
|
294
|
-
# is there any way to get deferrable status?
|
|
295
|
-
end
|
|
296
|
-
rescue Sequel::DatabaseError
|
|
297
|
-
# Doesn't work correctly on some versions of JDBC SQLite,
|
|
298
|
-
# giving a "query does not return ResultSet" error.
|
|
299
|
-
end
|
|
300
297
|
cols
|
|
301
298
|
end
|
|
302
299
|
|
|
@@ -319,6 +316,20 @@ module Sequel
|
|
|
319
316
|
def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
|
|
320
317
|
end
|
|
321
318
|
|
|
319
|
+
# If dropping a foreign key constraint, drop all foreign key constraints,
|
|
320
|
+
# as there is no way to determine which one to drop.
|
|
321
|
+
unless opts[:no_foreign_keys]
|
|
322
|
+
fks = foreign_key_list(table)
|
|
323
|
+
|
|
324
|
+
# If dropping a column, if there is a foreign key with that
|
|
325
|
+
# column, don't include it when building a copy of the table.
|
|
326
|
+
if ocp = opts[:old_columns_proc]
|
|
327
|
+
fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
constraints.concat(fks.each{|h| h[:type] = :foreign_key})
|
|
331
|
+
end
|
|
332
|
+
|
|
322
333
|
def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
|
|
323
334
|
new_columns = old_columns.dup
|
|
324
335
|
opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
|
|
@@ -399,13 +410,12 @@ module Sequel
|
|
|
399
410
|
m = output_identifier_meth
|
|
400
411
|
metadata_dataset.from(:sqlite_master).server(opts[:server]).filter(filter).map{|r| m.call(r[:name])}
|
|
401
412
|
end
|
|
402
|
-
|
|
403
|
-
# SQLite
|
|
404
|
-
#
|
|
405
|
-
#
|
|
406
|
-
# that software will automatically return the column as an integer.
|
|
413
|
+
|
|
414
|
+
# SQLite only supports AUTOINCREMENT on integer columns, not
|
|
415
|
+
# bigint columns, so use integer instead of bigint for those
|
|
416
|
+
# columns.
|
|
407
417
|
def type_literal_generic_bignum(column)
|
|
408
|
-
:integer
|
|
418
|
+
column[:auto_increment] ? :integer : super
|
|
409
419
|
end
|
|
410
420
|
end
|
|
411
421
|
|
|
@@ -424,6 +434,8 @@ module Sequel
|
|
|
424
434
|
NUMERIC = 'NUMERIC'.freeze
|
|
425
435
|
INTEGER = 'INTEGER'.freeze
|
|
426
436
|
BACKTICK = '`'.freeze
|
|
437
|
+
BACKTICK_RE = /`/.freeze
|
|
438
|
+
DOUBLE_BACKTICK = '``'.freeze
|
|
427
439
|
BLOB_START = "X'".freeze
|
|
428
440
|
HSTAR = "H*".freeze
|
|
429
441
|
|
|
@@ -470,14 +482,19 @@ module Sequel
|
|
|
470
482
|
# Since we want to always return the count of records, add a condition
|
|
471
483
|
# that is always true and then delete.
|
|
472
484
|
def delete
|
|
473
|
-
@opts[:where] ? super :
|
|
485
|
+
@opts[:where] ? super : where(1=>1).delete
|
|
474
486
|
end
|
|
475
487
|
|
|
476
488
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
|
477
|
-
# current dataset.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
489
|
+
# current dataset. Currently, the options are ignore, but it accepts options
|
|
490
|
+
# to be compatible with other adapters.
|
|
491
|
+
def explain(opts=nil)
|
|
492
|
+
# Load the PrettyTable class, needed for explain output
|
|
493
|
+
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
|
494
|
+
|
|
495
|
+
ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
|
|
496
|
+
rows = ds.all
|
|
497
|
+
Sequel::PrettyTable.string(rows, ds.columns)
|
|
481
498
|
end
|
|
482
499
|
|
|
483
500
|
# HAVING requires GROUP BY on SQLite
|
|
@@ -488,7 +505,7 @@ module Sequel
|
|
|
488
505
|
|
|
489
506
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
|
490
507
|
def quoted_identifier_append(sql, c)
|
|
491
|
-
sql << BACKTICK << c.to_s << BACKTICK
|
|
508
|
+
sql << BACKTICK << c.to_s.gsub(BACKTICK_RE, DOUBLE_BACKTICK) << BACKTICK
|
|
492
509
|
end
|
|
493
510
|
|
|
494
511
|
# When a qualified column is selected on SQLite and the qualifier
|