sequel 3.34.1 → 3.35.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|