sequel 3.34.1 → 3.35.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 +52 -0
- data/README.rdoc +3 -1
- data/Rakefile +2 -10
- data/doc/active_record.rdoc +1 -0
- data/doc/migration.rdoc +18 -7
- data/doc/model_hooks.rdoc +6 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/prepared_statements.rdoc +0 -1
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/schema_modification.rdoc +16 -1
- data/doc/thread_safety.rdoc +17 -0
- data/lib/sequel/adapters/do.rb +2 -2
- data/lib/sequel/adapters/do/postgres.rb +1 -52
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/firebird.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +23 -19
- data/lib/sequel/adapters/jdbc/db2.rb +0 -5
- data/lib/sequel/adapters/jdbc/derby.rb +29 -2
- data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
- data/lib/sequel/adapters/jdbc/informix.rb +0 -5
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
- data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
- data/lib/sequel/adapters/mysql2.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +3 -3
- data/lib/sequel/adapters/odbc/mssql.rb +14 -1
- data/lib/sequel/adapters/oracle.rb +6 -18
- data/lib/sequel/adapters/postgres.rb +36 -53
- data/lib/sequel/adapters/shared/db2.rb +16 -2
- data/lib/sequel/adapters/shared/mssql.rb +40 -9
- data/lib/sequel/adapters/shared/mysql.rb +16 -4
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
- data/lib/sequel/adapters/shared/oracle.rb +2 -0
- data/lib/sequel/adapters/shared/postgres.rb +135 -211
- data/lib/sequel/adapters/sqlite.rb +2 -2
- data/lib/sequel/adapters/swift.rb +1 -1
- data/lib/sequel/adapters/swift/postgres.rb +1 -71
- data/lib/sequel/adapters/tinytds.rb +3 -3
- data/lib/sequel/core.rb +27 -4
- data/lib/sequel/database/connecting.rb +7 -8
- data/lib/sequel/database/logging.rb +6 -1
- data/lib/sequel/database/misc.rb +20 -4
- data/lib/sequel/database/query.rb +38 -18
- data/lib/sequel/database/schema_generator.rb +5 -2
- data/lib/sequel/database/schema_methods.rb +34 -8
- data/lib/sequel/dataset/prepared_statements.rb +1 -1
- data/lib/sequel/dataset/sql.rb +18 -24
- data/lib/sequel/extensions/core_extensions.rb +0 -23
- data/lib/sequel/extensions/migration.rb +22 -8
- data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/model.rb +2 -2
- data/lib/sequel/model/associations.rb +95 -70
- data/lib/sequel/model/base.rb +16 -18
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/identity_map.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +16 -1
- data/lib/sequel/plugins/many_through_many.rb +22 -32
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
- data/lib/sequel/plugins/prepared_statements.rb +22 -8
- data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +12 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +2 -2
- data/spec/adapters/postgres_spec.rb +30 -79
- data/spec/core/database_spec.rb +46 -2
- data/spec/core/dataset_spec.rb +28 -22
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +51 -0
- data/spec/extensions/arbitrary_servers_spec.rb +0 -4
- data/spec/extensions/association_autoreloading_spec.rb +17 -0
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/core_extensions_spec.rb +1 -24
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/json_serializer_spec.rb +13 -0
- data/spec/extensions/migration_spec.rb +28 -15
- data/spec/extensions/named_timezones_spec.rb +6 -8
- data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
- data/spec/extensions/schema_dumper_spec.rb +3 -1
- data/spec/extensions/xml_serializer_spec.rb +13 -0
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
- data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
- data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
- data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
- data/spec/integration/associations_test.rb +5 -7
- data/spec/integration/dataset_test.rb +25 -7
- data/spec/integration/plugin_test.rb +1 -1
- data/spec/integration/schema_test.rb +16 -1
- data/spec/model/associations_spec.rb +2 -2
- metadata +14 -9
- data/lib/sequel/adapters/odbc/db2.rb +0 -17
|
@@ -41,7 +41,7 @@ module Sequel
|
|
|
41
41
|
# arguments.
|
|
42
42
|
def execute_prepared_statement(ps_name, opts, &block)
|
|
43
43
|
args = opts[:arguments]
|
|
44
|
-
ps =
|
|
44
|
+
ps = prepared_statement(ps_name)
|
|
45
45
|
sql = ps.prepared_sql
|
|
46
46
|
synchronize(opts[:server]) do |conn|
|
|
47
47
|
unless conn.prepared_statements[ps_name] == sql
|
|
@@ -137,7 +137,7 @@ module Sequel
|
|
|
137
137
|
ps.extend(PreparedStatementMethods)
|
|
138
138
|
if name
|
|
139
139
|
ps.prepared_statement_name = name
|
|
140
|
-
db.
|
|
140
|
+
db.set_prepared_statement(name, ps)
|
|
141
141
|
end
|
|
142
142
|
ps
|
|
143
143
|
end
|
|
@@ -217,6 +217,8 @@ module Sequel
|
|
|
217
217
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} * power(2, #{literal b}))"}
|
|
218
218
|
when :>>
|
|
219
219
|
sql << complex_expression_arg_pairs(args){|a, b| "(#{literal(a)} / power(2, #{literal b}))"}
|
|
220
|
+
when :%
|
|
221
|
+
sql << complex_expression_arg_pairs(args){|a, b| "MOD(#{literal(a)}, #{literal(b)})"}
|
|
220
222
|
when :ILIKE, :'NOT ILIKE'
|
|
221
223
|
sql << ILIKE_0
|
|
222
224
|
literal_append(sql, args.at(0))
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
module Sequel
|
|
2
|
-
Dataset::NON_SQL_OPTIONS << :disable_insert_returning
|
|
3
|
-
|
|
4
2
|
# Top level module for holding all PostgreSQL-related modules and classes
|
|
5
3
|
# for Sequel. There are a few module level accessors that are added via
|
|
6
4
|
# metaprogramming. These are:
|
|
@@ -45,25 +43,32 @@ module Sequel
|
|
|
45
43
|
attr_accessor :client_min_messages
|
|
46
44
|
|
|
47
45
|
# By default, Sequel forces the use of standard strings, so that
|
|
48
|
-
# '\\' is interpreted as \\ and not \. While PostgreSQL defaults
|
|
49
|
-
# to interpreting plain strings
|
|
50
|
-
#
|
|
51
|
-
#
|
|
46
|
+
# '\\' is interpreted as \\ and not \. While PostgreSQL <9.1 defaults
|
|
47
|
+
# to interpreting plain strings, newer versions use standard strings by
|
|
48
|
+
# default. Sequel assumes that SQL standard strings will be used. Setting
|
|
49
|
+
# this to false means Sequel will use the database's default.
|
|
52
50
|
attr_accessor :force_standard_strings
|
|
53
51
|
end
|
|
54
52
|
|
|
55
|
-
# Methods shared by
|
|
56
|
-
module
|
|
57
|
-
|
|
53
|
+
# Methods shared by Database instances that connect to PostgreSQL.
|
|
54
|
+
module DatabaseMethods
|
|
55
|
+
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
|
56
|
+
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
57
|
+
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
58
|
+
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
|
59
|
+
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
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
# SQL fragment for custom sequences (ones not created by serial primary key),
|
|
62
|
+
# Returning the schema and literal form of the sequence name, by parsing
|
|
63
|
+
# the column defaults table.
|
|
60
64
|
SELECT_CUSTOM_SEQUENCE_SQL = (<<-end_sql
|
|
61
|
-
SELECT
|
|
65
|
+
SELECT name.nspname AS "schema",
|
|
66
|
+
CASE
|
|
62
67
|
WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
|
|
63
68
|
substr(split_part(def.adsrc, '''', 2),
|
|
64
69
|
strpos(split_part(def.adsrc, '''', 2), '.')+1)
|
|
65
70
|
ELSE split_part(def.adsrc, '''', 2)
|
|
66
|
-
END
|
|
71
|
+
END AS "sequence"
|
|
67
72
|
FROM pg_class t
|
|
68
73
|
JOIN pg_namespace name ON (t.relnamespace = name.oid)
|
|
69
74
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
|
@@ -74,8 +79,10 @@ module Sequel
|
|
|
74
79
|
end_sql
|
|
75
80
|
).strip.gsub(/\s+/, ' ').freeze
|
|
76
81
|
|
|
82
|
+
# SQL fragment for determining primary key column for the given table. Only
|
|
83
|
+
# returns the first primary key if the table has a composite primary key.
|
|
77
84
|
SELECT_PK_SQL = (<<-end_sql
|
|
78
|
-
SELECT pg_attribute.attname
|
|
85
|
+
SELECT pg_attribute.attname AS pk
|
|
79
86
|
FROM pg_class, pg_attribute, pg_index, pg_namespace
|
|
80
87
|
WHERE pg_class.oid = pg_attribute.attrelid
|
|
81
88
|
AND pg_class.relnamespace = pg_namespace.oid
|
|
@@ -85,8 +92,10 @@ module Sequel
|
|
|
85
92
|
end_sql
|
|
86
93
|
).strip.gsub(/\s+/, ' ').freeze
|
|
87
94
|
|
|
95
|
+
# SQL fragment for getting sequence associated with table's
|
|
96
|
+
# primary key, assuming it was a serial primary key column.
|
|
88
97
|
SELECT_SERIAL_SEQUENCE_SQL = (<<-end_sql
|
|
89
|
-
SELECT
|
|
98
|
+
SELECT name.nspname AS "schema", seq.relname AS "sequence"
|
|
90
99
|
FROM pg_class seq, pg_attribute attr, pg_depend dep,
|
|
91
100
|
pg_namespace name, pg_constraint cons
|
|
92
101
|
WHERE seq.oid = dep.objid
|
|
@@ -100,72 +109,6 @@ module Sequel
|
|
|
100
109
|
end_sql
|
|
101
110
|
).strip.gsub(/\s+/, ' ').freeze
|
|
102
111
|
|
|
103
|
-
# Depth of the current transaction on this connection, used
|
|
104
|
-
# to implement multi-level transactions with savepoints.
|
|
105
|
-
attr_accessor :transaction_depth
|
|
106
|
-
|
|
107
|
-
# Apply connection settings for this connection. Currently, turns
|
|
108
|
-
# standard_conforming_strings ON if Postgres.force_standard_strings
|
|
109
|
-
# is true.
|
|
110
|
-
def apply_connection_settings
|
|
111
|
-
if Postgres.force_standard_strings
|
|
112
|
-
# This setting will only work on PostgreSQL 8.2 or greater
|
|
113
|
-
# and we don't know the server version at this point, so
|
|
114
|
-
# try it unconditionally and rescue any errors.
|
|
115
|
-
execute("SET standard_conforming_strings = ON") rescue nil
|
|
116
|
-
end
|
|
117
|
-
if cmm = Postgres.client_min_messages
|
|
118
|
-
execute("SET client_min_messages = '#{cmm.to_s.upcase}'")
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Get the last inserted value for the given sequence.
|
|
123
|
-
def last_insert_id(sequence)
|
|
124
|
-
sql = SELECT_CURRVAL % sequence
|
|
125
|
-
execute(sql) do |r|
|
|
126
|
-
val = single_value(r)
|
|
127
|
-
return val.to_i if val
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
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.
|
|
134
|
-
def primary_key(schema, table)
|
|
135
|
-
sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{table}"
|
|
136
|
-
sql << "AND pg_namespace.nspname = #{schema}" if schema
|
|
137
|
-
execute(sql) do |r|
|
|
138
|
-
return single_value(r)
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
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.
|
|
145
|
-
def sequence(schema, table)
|
|
146
|
-
sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
|
|
147
|
-
sql << " AND name.nspname = #{schema}" if schema
|
|
148
|
-
execute(sql) do |r|
|
|
149
|
-
seq = single_value(r)
|
|
150
|
-
return seq if seq
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
154
|
-
sql << " AND name.nspname = #{schema}" if schema
|
|
155
|
-
execute(sql) do |r|
|
|
156
|
-
return single_value(r)
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Methods shared by Database instances that connect to PostgreSQL.
|
|
162
|
-
module DatabaseMethods
|
|
163
|
-
EXCLUDE_SCHEMAS = /pg_*|information_schema/i
|
|
164
|
-
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
165
|
-
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session|relation "(.*)" does not exist/.freeze
|
|
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
|
|
168
|
-
|
|
169
112
|
# Commit an existing prepared transaction with the given transaction
|
|
170
113
|
# identifier string.
|
|
171
114
|
def commit_prepared_transaction(transaction_id)
|
|
@@ -340,12 +283,11 @@ module Sequel
|
|
|
340
283
|
join(:pg_index___ind, :indrelid=>:oid, im.call(table)=>:relname).
|
|
341
284
|
join(:pg_class___indc, :oid=>:indexrelid).
|
|
342
285
|
join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
|
|
343
|
-
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
|
|
286
|
+
filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true).
|
|
344
287
|
order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
|
|
345
288
|
select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
|
|
346
289
|
|
|
347
290
|
ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
|
|
348
|
-
ds.filter!(:indisvalid=>true) if server_version >= 80200
|
|
349
291
|
ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
|
|
350
292
|
|
|
351
293
|
indexes = {}
|
|
@@ -374,30 +316,39 @@ module Sequel
|
|
|
374
316
|
# Return primary key for the given table.
|
|
375
317
|
def primary_key(table, opts={})
|
|
376
318
|
quoted_table = quote_schema_table(table)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
319
|
+
@primary_keys.fetch(quoted_table) do
|
|
320
|
+
schema, table = schema_and_table(table)
|
|
321
|
+
sql = "#{SELECT_PK_SQL} AND pg_class.relname = #{literal(table)}"
|
|
322
|
+
sql << "AND pg_namespace.nspname = #{literal(schema)}" if schema
|
|
323
|
+
@primary_keys[quoted_table] = fetch(sql).single_value
|
|
382
324
|
end
|
|
383
325
|
end
|
|
384
326
|
|
|
385
327
|
# Return the sequence providing the default for the primary key for the given table.
|
|
386
328
|
def primary_key_sequence(table, opts={})
|
|
387
329
|
quoted_table = quote_schema_table(table)
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
330
|
+
@primary_key_sequences.fetch(quoted_table) do
|
|
331
|
+
schema, table = schema_and_table(table)
|
|
332
|
+
table = literal(table)
|
|
333
|
+
sql = "#{SELECT_SERIAL_SEQUENCE_SQL} AND seq.relname = #{table}"
|
|
334
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
335
|
+
unless pks = fetch(sql).single_record
|
|
336
|
+
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.relname = #{table}"
|
|
337
|
+
sql << " AND name.nspname = #{literal(schema)}" if schema
|
|
338
|
+
pks = fetch(sql).single_record
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
@primary_key_sequences[quoted_table] = if pks
|
|
342
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
343
|
+
end
|
|
393
344
|
end
|
|
394
345
|
end
|
|
395
346
|
|
|
396
347
|
# Reset the primary key sequence for the given table, baseing it on the
|
|
397
348
|
# maximum current value of the table's primary key.
|
|
398
349
|
def reset_primary_key_sequence(table)
|
|
399
|
-
pk = SQL::Identifier.new(primary_key(table))
|
|
400
350
|
return unless seq = primary_key_sequence(table)
|
|
351
|
+
pk = SQL::Identifier.new(primary_key(table))
|
|
401
352
|
db = self
|
|
402
353
|
seq_ds = db.from(LiteralString.new(seq))
|
|
403
354
|
get{setval(seq, db[table].select{coalesce(max(pk)+seq_ds.select{:increment_by}, seq_ds.select(:min_value))}, false)}
|
|
@@ -428,25 +379,25 @@ module Sequel
|
|
|
428
379
|
0
|
|
429
380
|
end
|
|
430
381
|
end
|
|
431
|
-
warn 'Sequel
|
|
382
|
+
warn 'Sequel no longer supports PostgreSQL <8.2, some things may not work' if @server_version < 80200
|
|
432
383
|
@server_version
|
|
433
384
|
end
|
|
434
385
|
|
|
435
|
-
# PostgreSQL supports prepared transactions (two-phase commit) if
|
|
436
|
-
# max_prepared_transactions is greater than 0.
|
|
437
|
-
def supports_prepared_transactions?
|
|
438
|
-
return @supports_prepared_transactions if defined?(@supports_prepared_transactions)
|
|
439
|
-
@supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
|
|
440
|
-
end
|
|
441
|
-
|
|
442
386
|
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
|
443
387
|
def supports_create_table_if_not_exists?
|
|
444
388
|
server_version >= 90100
|
|
445
389
|
end
|
|
446
390
|
|
|
447
|
-
# PostgreSQL supports DROP TABLE IF EXISTS
|
|
391
|
+
# PostgreSQL supports DROP TABLE IF EXISTS
|
|
448
392
|
def supports_drop_table_if_exists?
|
|
449
|
-
|
|
393
|
+
true
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# PostgreSQL supports prepared transactions (two-phase commit) if
|
|
397
|
+
# max_prepared_transactions is greater than 0.
|
|
398
|
+
def supports_prepared_transactions?
|
|
399
|
+
return @supports_prepared_transactions if defined?(@supports_prepared_transactions)
|
|
400
|
+
@supports_prepared_transactions = self['SHOW max_prepared_transactions'].get.to_i > 0
|
|
450
401
|
end
|
|
451
402
|
|
|
452
403
|
# PostgreSQL supports savepoints
|
|
@@ -459,6 +410,11 @@ module Sequel
|
|
|
459
410
|
true
|
|
460
411
|
end
|
|
461
412
|
|
|
413
|
+
# PostgreSQL supports transaction DDL statements.
|
|
414
|
+
def supports_transactional_ddl?
|
|
415
|
+
true
|
|
416
|
+
end
|
|
417
|
+
|
|
462
418
|
# Array of symbols specifying table names in the current database.
|
|
463
419
|
# The dataset used is yielded to the block if one is provided,
|
|
464
420
|
# otherwise, an array of symbols of table names is returned.
|
|
@@ -491,13 +447,23 @@ module Sequel
|
|
|
491
447
|
# If the :prepare option is given and we aren't in a savepoint,
|
|
492
448
|
# prepare the transaction for a two-phase commit.
|
|
493
449
|
def commit_transaction(conn, opts={})
|
|
494
|
-
if (s = opts[:prepare]) &&
|
|
450
|
+
if (s = opts[:prepare]) && _trans(conn)[:savepoint_level] <= 1
|
|
495
451
|
log_connection_execute(conn, "PREPARE TRANSACTION #{literal(s)}")
|
|
496
452
|
else
|
|
497
453
|
super
|
|
498
454
|
end
|
|
499
455
|
end
|
|
500
456
|
|
|
457
|
+
# The SQL queries to execute when starting a new connection.
|
|
458
|
+
def connection_configuration_sqls
|
|
459
|
+
sqls = []
|
|
460
|
+
sqls << "SET standard_conforming_strings = ON" if Postgres.force_standard_strings
|
|
461
|
+
if cmm = Postgres.client_min_messages
|
|
462
|
+
sqls << "SET client_min_messages = '#{cmm.to_s.upcase}'"
|
|
463
|
+
end
|
|
464
|
+
sqls
|
|
465
|
+
end
|
|
466
|
+
|
|
501
467
|
# SQL statement to create database function.
|
|
502
468
|
def create_function_sql(name, definition, opts={})
|
|
503
469
|
args = opts[:args]
|
|
@@ -604,39 +570,6 @@ module Sequel
|
|
|
604
570
|
"CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
|
605
571
|
end
|
|
606
572
|
|
|
607
|
-
# The result of the insert for the given table and values. If values
|
|
608
|
-
# is an array, assume the first column is the primary key and return
|
|
609
|
-
# that. If values is a hash, lookup the primary key for the table. If
|
|
610
|
-
# the primary key is present in the hash, return its value. Otherwise,
|
|
611
|
-
# look up the sequence for the table's primary key. If one exists,
|
|
612
|
-
# return the last value the of the sequence for the connection.
|
|
613
|
-
def insert_result(conn, table, values)
|
|
614
|
-
case values
|
|
615
|
-
when Hash
|
|
616
|
-
return nil unless pk = primary_key(table, :conn=>conn)
|
|
617
|
-
if pk and pkv = values[pk.to_sym]
|
|
618
|
-
pkv
|
|
619
|
-
else
|
|
620
|
-
begin
|
|
621
|
-
if seq = primary_key_sequence(table, :conn=>conn)
|
|
622
|
-
conn.last_insert_id(seq)
|
|
623
|
-
end
|
|
624
|
-
rescue Exception => e
|
|
625
|
-
raise_error(e, :classes=>CONVERTED_EXCEPTIONS) unless RE_CURRVAL_ERROR.match(e.message)
|
|
626
|
-
end
|
|
627
|
-
end
|
|
628
|
-
when Array
|
|
629
|
-
values.first
|
|
630
|
-
else
|
|
631
|
-
nil
|
|
632
|
-
end
|
|
633
|
-
end
|
|
634
|
-
|
|
635
|
-
# Don't log, since logging is done by the underlying connection.
|
|
636
|
-
def log_connection_execute(conn, sql)
|
|
637
|
-
conn.execute(sql)
|
|
638
|
-
end
|
|
639
|
-
|
|
640
573
|
# Backbone of the tables and views support.
|
|
641
574
|
def pg_class_relname(type, opts)
|
|
642
575
|
ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
|
@@ -666,13 +599,6 @@ module Sequel
|
|
|
666
599
|
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
|
667
600
|
end
|
|
668
601
|
|
|
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
|
-
|
|
676
602
|
# PostgreSQL's autoincrementing primary keys are of type integer or bigint
|
|
677
603
|
# using a nextval function call as a default.
|
|
678
604
|
def schema_autoincrementing_primary_key?(schema)
|
|
@@ -758,15 +684,13 @@ module Sequel
|
|
|
758
684
|
BOOL_FALSE = 'false'.freeze
|
|
759
685
|
BOOL_TRUE = 'true'.freeze
|
|
760
686
|
COMMA_SEPARATOR = ', '.freeze
|
|
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')
|
|
687
|
+
DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'delete from using where returning')
|
|
763
688
|
DELETE_CLAUSE_METHODS_91 = Dataset.clause_methods(:delete, %w'with delete from using where returning')
|
|
764
689
|
EXCLUSIVE = 'EXCLUSIVE'.freeze
|
|
765
690
|
EXPLAIN = 'EXPLAIN '.freeze
|
|
766
691
|
EXPLAIN_ANALYZE = 'EXPLAIN ANALYZE '.freeze
|
|
767
692
|
FOR_SHARE = ' FOR SHARE'.freeze
|
|
768
|
-
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values')
|
|
769
|
-
INSERT_CLAUSE_METHODS_82 = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
693
|
+
INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'insert into columns values returning')
|
|
770
694
|
INSERT_CLAUSE_METHODS_91 = Dataset.clause_methods(:insert, %w'with insert into columns values returning')
|
|
771
695
|
LOCK = 'LOCK TABLE %s IN %s MODE'.freeze
|
|
772
696
|
NULL = LiteralString.new('NULL').freeze
|
|
@@ -780,8 +704,7 @@ module Sequel
|
|
|
780
704
|
SHARE_ROW_EXCLUSIVE = 'SHARE ROW EXCLUSIVE'.freeze
|
|
781
705
|
SHARE_UPDATE_EXCLUSIVE = 'SHARE UPDATE EXCLUSIVE'.freeze
|
|
782
706
|
SQL_WITH_RECURSIVE = "WITH RECURSIVE ".freeze
|
|
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')
|
|
707
|
+
UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'update table set from where returning')
|
|
785
708
|
UPDATE_CLAUSE_METHODS_91 = Dataset.clause_methods(:update, %w'with update table set from where returning')
|
|
786
709
|
SPACE = Dataset::SPACE
|
|
787
710
|
FROM = Dataset::FROM
|
|
@@ -802,7 +725,7 @@ module Sequel
|
|
|
802
725
|
module PreparedStatementMethods
|
|
803
726
|
# Override insert action to use RETURNING if the server supports it.
|
|
804
727
|
def run
|
|
805
|
-
if @prepared_type == :insert
|
|
728
|
+
if @prepared_type == :insert
|
|
806
729
|
fetch_rows(prepared_sql){|r| return r.values.first}
|
|
807
730
|
else
|
|
808
731
|
super
|
|
@@ -811,22 +734,12 @@ module Sequel
|
|
|
811
734
|
|
|
812
735
|
def prepared_sql
|
|
813
736
|
return @prepared_sql if @prepared_sql
|
|
814
|
-
@opts[:returning] = insert_pk if @prepared_type == :insert
|
|
737
|
+
@opts[:returning] = insert_pk if @prepared_type == :insert
|
|
815
738
|
super
|
|
816
739
|
@prepared_sql
|
|
817
740
|
end
|
|
818
741
|
end
|
|
819
742
|
|
|
820
|
-
# Add the disable_insert_returning! mutation method
|
|
821
|
-
def self.extended(obj)
|
|
822
|
-
obj.def_mutation_method(:disable_insert_returning)
|
|
823
|
-
end
|
|
824
|
-
|
|
825
|
-
# Add the disable_insert_returning! mutation method
|
|
826
|
-
def self.included(mod)
|
|
827
|
-
mod.def_mutation_method(:disable_insert_returning)
|
|
828
|
-
end
|
|
829
|
-
|
|
830
743
|
# Return the results of an EXPLAIN ANALYZE query as a string
|
|
831
744
|
def analyze
|
|
832
745
|
explain(:analyze=>true)
|
|
@@ -849,12 +762,6 @@ module Sequel
|
|
|
849
762
|
end
|
|
850
763
|
end
|
|
851
764
|
|
|
852
|
-
# Disable the use of INSERT RETURNING, even if the server supports it
|
|
853
|
-
def disable_insert_returning
|
|
854
|
-
warn("disable_insert_returning is deprecated and will be removed in Sequel 3.35.0")
|
|
855
|
-
clone(:disable_insert_returning=>true)
|
|
856
|
-
end
|
|
857
|
-
|
|
858
765
|
# Return the results of an EXPLAIN query as a string
|
|
859
766
|
def explain(opts={})
|
|
860
767
|
with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
|
|
@@ -876,26 +783,23 @@ module Sequel
|
|
|
876
783
|
# Insert given values into the database.
|
|
877
784
|
def insert(*values)
|
|
878
785
|
if @opts[:returning]
|
|
786
|
+
# already know which columns to return, let the standard code
|
|
787
|
+
# handle it
|
|
879
788
|
super
|
|
880
|
-
elsif
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
values.first
|
|
885
|
-
elsif values.size == 2 && values.all?{|v0| v0.is_a?(Array)}
|
|
886
|
-
Hash[*values.first.zip(values.last).flatten]
|
|
887
|
-
else
|
|
888
|
-
values
|
|
889
|
-
end
|
|
890
|
-
execute_insert(insert_sql(*values), :table=>f.first, :values=>v)
|
|
891
|
-
else
|
|
789
|
+
elsif @opts[:sql]
|
|
790
|
+
# raw SQL used, so don't know which table is being inserted
|
|
791
|
+
# into, and therefore can't determine primary key. Run the
|
|
792
|
+
# insert statement and return nil.
|
|
892
793
|
super
|
|
794
|
+
nil
|
|
795
|
+
else
|
|
796
|
+
# Force the use of RETURNING with the primary key value.
|
|
797
|
+
returning(insert_pk).insert(*values){|r| return r.values.first}
|
|
893
798
|
end
|
|
894
799
|
end
|
|
895
800
|
|
|
896
801
|
# Insert a record returning the record inserted
|
|
897
802
|
def insert_select(*values)
|
|
898
|
-
return unless supports_insert_select?
|
|
899
803
|
returning.insert(*values){|r| return r}
|
|
900
804
|
end
|
|
901
805
|
|
|
@@ -913,11 +817,8 @@ module Sequel
|
|
|
913
817
|
nil
|
|
914
818
|
end
|
|
915
819
|
|
|
916
|
-
#
|
|
820
|
+
# PostgreSQL allows inserting multiple rows at once.
|
|
917
821
|
def multi_insert_sql(columns, values)
|
|
918
|
-
return super if server_version < 80200
|
|
919
|
-
|
|
920
|
-
# postgresql 8.2 introduces support for multi-row insert
|
|
921
822
|
sql = LiteralString.new('VALUES ')
|
|
922
823
|
expression_list_append(sql, values.map{|r| Array(r)})
|
|
923
824
|
[insert_sql(columns, sql)]
|
|
@@ -939,12 +840,9 @@ module Sequel
|
|
|
939
840
|
true
|
|
940
841
|
end
|
|
941
842
|
|
|
843
|
+
# Returning is always supported.
|
|
942
844
|
def supports_returning?(type)
|
|
943
|
-
|
|
944
|
-
server_version >= 80200 && !opts[:disable_insert_returning]
|
|
945
|
-
else
|
|
946
|
-
server_version >= 80200
|
|
947
|
-
end
|
|
845
|
+
true
|
|
948
846
|
end
|
|
949
847
|
|
|
950
848
|
# PostgreSQL supports timezones in literal timestamps
|
|
@@ -957,6 +855,29 @@ module Sequel
|
|
|
957
855
|
server_version >= 80400
|
|
958
856
|
end
|
|
959
857
|
|
|
858
|
+
# Truncates the dataset. Returns nil.
|
|
859
|
+
#
|
|
860
|
+
# Options:
|
|
861
|
+
# :cascade :: whether to use the CASCADE option, useful when truncating
|
|
862
|
+
# tables with Foreign Keys.
|
|
863
|
+
# :only :: truncate using ONLY, so child tables are unaffected
|
|
864
|
+
# :restart :: use RESTART IDENTITY to restart any related sequences
|
|
865
|
+
#
|
|
866
|
+
# :only and :restart only work correctly on PostgreSQL 8.4+.
|
|
867
|
+
#
|
|
868
|
+
# Usage:
|
|
869
|
+
# DB[:table].truncate # TRUNCATE TABLE "table"
|
|
870
|
+
# # => nil
|
|
871
|
+
# DB[:table].truncate(:cascade => true, :only=>true, :restart=>true) # TRUNCATE TABLE ONLY "table" RESTART IDENTITY CASCADE
|
|
872
|
+
# # => nil
|
|
873
|
+
def truncate(opts = {})
|
|
874
|
+
if opts.empty?
|
|
875
|
+
super()
|
|
876
|
+
else
|
|
877
|
+
clone(:truncate_opts=>opts).truncate
|
|
878
|
+
end
|
|
879
|
+
end
|
|
880
|
+
|
|
960
881
|
# Return a clone of the dataset with an addition named window that can be referenced in window functions.
|
|
961
882
|
def window(name, opts)
|
|
962
883
|
clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
|
|
@@ -969,17 +890,13 @@ module Sequel
|
|
|
969
890
|
# is only set to return a single columns, return an array of just that column.
|
|
970
891
|
# Otherwise, return an array of hashes.
|
|
971
892
|
def _import(columns, values, opts={})
|
|
972
|
-
if
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
980
|
-
else
|
|
981
|
-
super
|
|
982
|
-
end
|
|
893
|
+
if @opts[:returning]
|
|
894
|
+
statements = multi_insert_sql(columns, values)
|
|
895
|
+
@db.transaction(opts.merge(:server=>@opts[:server])) do
|
|
896
|
+
statements.map{|st| returning_fetch_rows(st)}
|
|
897
|
+
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
898
|
+
elsif opts[:return] == :primary_key
|
|
899
|
+
returning(insert_pk)._import(columns, values, opts)
|
|
983
900
|
else
|
|
984
901
|
super
|
|
985
902
|
end
|
|
@@ -987,12 +904,22 @@ module Sequel
|
|
|
987
904
|
|
|
988
905
|
private
|
|
989
906
|
|
|
907
|
+
# Format TRUNCATE statement with PostgreSQL specific options.
|
|
908
|
+
def _truncate_sql(table)
|
|
909
|
+
to = @opts[:truncate_opts] || {}
|
|
910
|
+
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
# Allow truncation of multiple source tables.
|
|
914
|
+
def check_truncation_allowed!
|
|
915
|
+
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
|
916
|
+
raise(InvalidOperation, "Joined datasets cannot be truncated") if opts[:join]
|
|
917
|
+
end
|
|
918
|
+
|
|
990
919
|
# PostgreSQL allows deleting from joined datasets
|
|
991
920
|
def delete_clause_methods
|
|
992
|
-
if
|
|
921
|
+
if server_version >= 90100
|
|
993
922
|
DELETE_CLAUSE_METHODS_91
|
|
994
|
-
elsif sv >= 80200
|
|
995
|
-
DELETE_CLAUSE_METHODS_82
|
|
996
923
|
else
|
|
997
924
|
DELETE_CLAUSE_METHODS
|
|
998
925
|
end
|
|
@@ -1013,8 +940,6 @@ module Sequel
|
|
|
1013
940
|
def insert_clause_methods
|
|
1014
941
|
if server_version >= 90100
|
|
1015
942
|
INSERT_CLAUSE_METHODS_91
|
|
1016
|
-
elsif server_version >= 80200
|
|
1017
|
-
INSERT_CLAUSE_METHODS_82
|
|
1018
943
|
else
|
|
1019
944
|
INSERT_CLAUSE_METHODS
|
|
1020
945
|
end
|
|
@@ -1022,8 +947,9 @@ module Sequel
|
|
|
1022
947
|
|
|
1023
948
|
# Return the primary key to use for RETURNING in an INSERT statement
|
|
1024
949
|
def insert_pk
|
|
1025
|
-
|
|
1026
|
-
|
|
950
|
+
if (f = opts[:from]) && !f.empty? && (pk = db.primary_key(f.first))
|
|
951
|
+
Sequel::SQL::Identifier.new(pk)
|
|
952
|
+
end
|
|
1027
953
|
end
|
|
1028
954
|
|
|
1029
955
|
# For multiple table support, PostgreSQL requires at least
|
|
@@ -1126,10 +1052,8 @@ module Sequel
|
|
|
1126
1052
|
|
|
1127
1053
|
# PostgreSQL splits the main table from the joined tables
|
|
1128
1054
|
def update_clause_methods
|
|
1129
|
-
if
|
|
1055
|
+
if server_version >= 90100
|
|
1130
1056
|
UPDATE_CLAUSE_METHODS_91
|
|
1131
|
-
elsif sv >= 80200
|
|
1132
|
-
UPDATE_CLAUSE_METHODS_82
|
|
1133
1057
|
else
|
|
1134
1058
|
UPDATE_CLAUSE_METHODS
|
|
1135
1059
|
end
|