sequel 2.11.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +168 -0
- data/README.rdoc +77 -95
- data/Rakefile +100 -80
- data/bin/sequel +2 -1
- data/doc/advanced_associations.rdoc +23 -32
- data/doc/cheat_sheet.rdoc +23 -40
- data/doc/dataset_filtering.rdoc +6 -6
- data/doc/prepared_statements.rdoc +22 -22
- data/doc/release_notes/2.12.0.txt +534 -0
- data/doc/schema.rdoc +3 -1
- data/doc/sharding.rdoc +8 -8
- data/doc/virtual_rows.rdoc +65 -0
- data/lib/sequel.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
- data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
- data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
- data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
- data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
- data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
- data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
- data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
- data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
- data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
- data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
- data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
- data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
- data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
- data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
- data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
- data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
- data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
- data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
- data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
- data/lib/sequel/core.rb +221 -0
- data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
- data/lib/{sequel_core → sequel}/database.rb +264 -149
- data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
- data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
- data/lib/sequel/database/schema_sql.rb +224 -0
- data/lib/{sequel_core → sequel}/dataset.rb +78 -236
- data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
- data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
- data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
- data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
- data/lib/sequel/deprecated.rb +593 -0
- data/lib/sequel/deprecated_migration.rb +91 -0
- data/lib/sequel/exceptions.rb +48 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
- data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
- data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
- data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
- data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +43 -0
- data/lib/sequel/model.rb +110 -0
- data/lib/sequel/model/associations.rb +1300 -0
- data/lib/sequel/model/base.rb +937 -0
- data/lib/sequel/model/deprecated.rb +204 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +388 -0
- data/lib/sequel/model/errors.rb +39 -0
- data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
- data/lib/sequel/model/inflections.rb +208 -0
- data/lib/sequel/model/plugins.rb +76 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/serialization.rb +117 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +384 -0
- data/lib/sequel/plugins/validation_helpers.rb +150 -0
- data/lib/{sequel_core → sequel}/sql.rb +125 -190
- data/lib/{sequel_core → sequel}/version.rb +2 -1
- data/lib/sequel_core.rb +1 -172
- data/lib/sequel_model.rb +1 -91
- data/spec/adapters/firebird_spec.rb +5 -5
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mysql_spec.rb +128 -42
- data/spec/adapters/oracle_spec.rb +47 -19
- data/spec/adapters/postgres_spec.rb +64 -52
- data/spec/adapters/spec_helper.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +12 -17
- data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
- data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
- data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
- data/spec/{sequel_core → core}/database_spec.rb +135 -99
- data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
- data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
- data/spec/core/migration_spec.rb +263 -0
- data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
- data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
- data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
- data/spec/{sequel_core → core}/schema_spec.rb +8 -10
- data/spec/{sequel_core → core}/spec_helper.rb +29 -2
- data/spec/{sequel_core → core}/version_spec.rb +0 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
- data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
- data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
- data/spec/extensions/serialization_spec.rb +109 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
- data/spec/extensions/validation_helpers_spec.rb +291 -0
- data/spec/integration/dataset_test.rb +31 -0
- data/spec/integration/eager_loader_test.rb +17 -30
- data/spec/integration/schema_test.rb +8 -5
- data/spec/integration/spec_helper.rb +17 -0
- data/spec/integration/transaction_test.rb +68 -0
- data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
- data/spec/{sequel_model → model}/associations_spec.rb +23 -10
- data/spec/{sequel_model → model}/base_spec.rb +29 -20
- data/spec/{sequel_model → model}/caching_spec.rb +16 -14
- data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
- data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/{sequel_model → model}/model_spec.rb +25 -20
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/{sequel_model → model}/record_spec.rb +121 -62
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- metadata +136 -107
- data/lib/sequel_core/core_ext.rb +0 -217
- data/lib/sequel_core/dataset/callback.rb +0 -13
- data/lib/sequel_core/dataset/schema.rb +0 -15
- data/lib/sequel_core/deprecated.rb +0 -26
- data/lib/sequel_core/exceptions.rb +0 -44
- data/lib/sequel_core/schema.rb +0 -2
- data/lib/sequel_core/schema/sql.rb +0 -325
- data/lib/sequel_model/association_reflection.rb +0 -267
- data/lib/sequel_model/associations.rb +0 -499
- data/lib/sequel_model/base.rb +0 -539
- data/lib/sequel_model/caching.rb +0 -82
- data/lib/sequel_model/dataset_methods.rb +0 -26
- data/lib/sequel_model/eager_loading.rb +0 -370
- data/lib/sequel_model/hooks.rb +0 -101
- data/lib/sequel_model/plugins.rb +0 -62
- data/lib/sequel_model/record.rb +0 -568
- data/lib/sequel_model/schema.rb +0 -49
- data/lib/sequel_model/validations.rb +0 -429
- data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
Sequel.require 'adapters/utils/unsupported'
|
2
2
|
|
3
3
|
module Sequel
|
4
4
|
module MSSQL
|
@@ -8,10 +8,6 @@ module Sequel
|
|
8
8
|
SQL_COMMIT = "COMMIT TRANSACTION".freeze
|
9
9
|
SQL_ROLLBACK = "ROLLBACK TRANSACTION".freeze
|
10
10
|
|
11
|
-
def auto_increment_sql
|
12
|
-
AUTO_INCREMENT
|
13
|
-
end
|
14
|
-
|
15
11
|
def dataset(opts = nil)
|
16
12
|
ds = super
|
17
13
|
ds.extend(DatasetMethods)
|
@@ -20,6 +16,10 @@ module Sequel
|
|
20
16
|
|
21
17
|
private
|
22
18
|
|
19
|
+
def auto_increment_sql
|
20
|
+
AUTO_INCREMENT
|
21
|
+
end
|
22
|
+
|
23
23
|
# SQL to BEGIN a transaction.
|
24
24
|
def begin_transaction_sql
|
25
25
|
SQL_BEGIN
|
@@ -1,27 +1,55 @@
|
|
1
|
-
require '
|
1
|
+
Sequel.require 'adapters/utils/unsupported'
|
2
2
|
|
3
3
|
module Sequel
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
alias default_column_references_sql column_references_sql
|
8
|
-
end
|
4
|
+
class Database
|
5
|
+
# Keep default column_references_sql for add_foreign_key support
|
6
|
+
alias default_column_references_sql column_references_sql
|
9
7
|
end
|
10
8
|
module MySQL
|
11
|
-
|
12
|
-
|
9
|
+
class << self
|
10
|
+
# Set the default options used for CREATE TABLE
|
11
|
+
attr_accessor :default_charset, :default_collate, :default_engine
|
12
|
+
end
|
13
13
|
|
14
14
|
# Methods shared by Database instances that connect to MySQL,
|
15
15
|
# currently supported by the native and JDBC adapters.
|
16
16
|
module DatabaseMethods
|
17
17
|
AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
|
18
|
-
NOT_NULL = Sequel::
|
19
|
-
NULL = Sequel::
|
20
|
-
PRIMARY_KEY = Sequel::
|
21
|
-
TYPES = Sequel::
|
18
|
+
NOT_NULL = Sequel::Database::NOT_NULL
|
19
|
+
NULL = Sequel::Database::NULL
|
20
|
+
PRIMARY_KEY = Sequel::Database::PRIMARY_KEY
|
21
|
+
TYPES = Sequel::Database::TYPES.merge(DateTime=>'datetime', \
|
22
22
|
TrueClass=>'tinyint', FalseClass=>'tinyint')
|
23
|
-
UNIQUE = Sequel::
|
24
|
-
UNSIGNED = Sequel::
|
23
|
+
UNIQUE = Sequel::Database::UNIQUE
|
24
|
+
UNSIGNED = Sequel::Database::UNSIGNED
|
25
|
+
|
26
|
+
# Get version of MySQL server, used for determined capabilities.
|
27
|
+
def server_version
|
28
|
+
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
29
|
+
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return an array of symbols specifying table names in the current database.
|
33
|
+
#
|
34
|
+
# Options:
|
35
|
+
# * :server - Set the server to use
|
36
|
+
def tables(opts={})
|
37
|
+
ds = self['SHOW TABLES'].server(opts[:server])
|
38
|
+
ds.identifier_output_method = nil
|
39
|
+
ds2 = dataset
|
40
|
+
ds.map{|r| ds2.send(:output_identifier, r.values.first)}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Changes the database in use by issuing a USE statement. I would be
|
44
|
+
# very careful if I used this.
|
45
|
+
def use(db_name)
|
46
|
+
disconnect
|
47
|
+
@opts[:database] = db_name if self << "USE #{db_name}"
|
48
|
+
@schemas = nil
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
25
53
|
|
26
54
|
# Use MySQL specific syntax for rename column, set column type, and
|
27
55
|
# drop index cases.
|
@@ -66,6 +94,16 @@ module Sequel
|
|
66
94
|
sql
|
67
95
|
end
|
68
96
|
|
97
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
98
|
+
def identifier_input_method_default
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
103
|
+
def identifier_output_method_default
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
69
107
|
# Handle MySQL specific index SQL syntax
|
70
108
|
def index_definition_sql(table_name, index)
|
71
109
|
index_name = quote_identifier(index[:name] || default_index_name(table_name, index[:columns]))
|
@@ -81,44 +119,6 @@ module Sequel
|
|
81
119
|
"CREATE #{index_type}INDEX #{index_name} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}#{using}"
|
82
120
|
end
|
83
121
|
|
84
|
-
# Get version of MySQL server, used for determined capabilities.
|
85
|
-
def server_version
|
86
|
-
m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
|
87
|
-
@server_version ||= (m[1].to_i * 10000) + (m[2].to_i * 100) + m[3].to_i
|
88
|
-
end
|
89
|
-
|
90
|
-
# Return an array of symbols specifying table names in the current database.
|
91
|
-
#
|
92
|
-
# Options:
|
93
|
-
# * :server - Set the server to use
|
94
|
-
def tables(opts={})
|
95
|
-
ds = self['SHOW TABLES'].server(opts[:server])
|
96
|
-
ds.identifier_output_method = nil
|
97
|
-
ds2 = dataset
|
98
|
-
ds.map{|r| ds2.send(:output_identifier, r.values.first)}
|
99
|
-
end
|
100
|
-
|
101
|
-
# Changes the database in use by issuing a USE statement. I would be
|
102
|
-
# very careful if I used this.
|
103
|
-
def use(db_name)
|
104
|
-
disconnect
|
105
|
-
@opts[:database] = db_name if self << "USE #{db_name}"
|
106
|
-
@schemas = nil
|
107
|
-
self
|
108
|
-
end
|
109
|
-
|
110
|
-
private
|
111
|
-
|
112
|
-
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
113
|
-
def identifier_input_method_default
|
114
|
-
nil
|
115
|
-
end
|
116
|
-
|
117
|
-
# MySQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
|
118
|
-
def identifier_output_method_default
|
119
|
-
nil
|
120
|
-
end
|
121
|
-
|
122
122
|
# Use the MySQL specific DESCRIBE syntax to get a table description.
|
123
123
|
def schema_parse_table(table_name, opts)
|
124
124
|
ds = self["DESCRIBE ?", SQL::Identifier.new(table_name)]
|
@@ -129,7 +129,7 @@ module Sequel
|
|
129
129
|
row[:allow_null] = row.delete(:Null) == 'YES'
|
130
130
|
row[:default] = row.delete(:Default)
|
131
131
|
row[:primary_key] = row.delete(:Key) == 'PRI'
|
132
|
-
row[:default] = nil if row[:default]
|
132
|
+
row[:default] = nil if blank_object?(row[:default])
|
133
133
|
row[:db_type] = row.delete(:Type)
|
134
134
|
row[:type] = schema_column_type(row[:db_type])
|
135
135
|
[ds2.send(:output_identifier, row.delete(:Field)), row]
|
@@ -175,9 +175,14 @@ module Sequel
|
|
175
175
|
end
|
176
176
|
|
177
177
|
# MySQL supports ORDER and LIMIT clauses in DELETE statements.
|
178
|
-
def delete_sql(opts = nil)
|
179
|
-
|
180
|
-
|
178
|
+
def delete_sql(opts = (defarg=true;nil))
|
179
|
+
if defarg
|
180
|
+
sql = super()
|
181
|
+
opts = @opts
|
182
|
+
else
|
183
|
+
sql = super
|
184
|
+
opts = opts ? @opts.merge(opts) : @opts
|
185
|
+
end
|
181
186
|
|
182
187
|
if order = opts[:order]
|
183
188
|
sql << " ORDER BY #{expression_list(order)}"
|
@@ -189,11 +194,17 @@ module Sequel
|
|
189
194
|
sql
|
190
195
|
end
|
191
196
|
|
197
|
+
# MySQL doesn't support DISTINCT ON
|
198
|
+
def distinct(*columns)
|
199
|
+
raise(Error, "DISTINCT ON not supported by MySQL") unless columns.empty?
|
200
|
+
super
|
201
|
+
end
|
202
|
+
|
192
203
|
# MySQL specific full text search syntax.
|
193
204
|
def full_text_search(cols, terms, opts = {})
|
194
205
|
mode = opts[:boolean] ? " IN BOOLEAN MODE" : ""
|
195
206
|
s = if Array === terms
|
196
|
-
if mode.
|
207
|
+
if mode.empty?
|
197
208
|
"MATCH #{literal(Array(cols))} AGAINST #{literal(terms)}"
|
198
209
|
else
|
199
210
|
"MATCH #{literal(Array(cols))} AGAINST (#{literal(terms)[1...-1]}#{mode})"
|
@@ -206,8 +217,7 @@ module Sequel
|
|
206
217
|
|
207
218
|
# MySQL allows HAVING clause on ungrouped datasets.
|
208
219
|
def having(*cond, &block)
|
209
|
-
|
210
|
-
x = filter(*cond, &block)
|
220
|
+
_filter(:having, *cond, &block)
|
211
221
|
end
|
212
222
|
|
213
223
|
# MySQL doesn't use the SQL standard DEFAULT VALUES.
|
@@ -233,10 +243,58 @@ module Sequel
|
|
233
243
|
end
|
234
244
|
end
|
235
245
|
|
246
|
+
# Sets up multi_insert or import to use INSERT IGNORE.
|
247
|
+
# Useful if you have a unique key and want to just skip
|
248
|
+
# inserting rows that violate the unique key restriction.
|
249
|
+
#
|
250
|
+
# Example:
|
251
|
+
#
|
252
|
+
# dataset.insert_ignore.multi_insert(
|
253
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
254
|
+
# )
|
255
|
+
#
|
256
|
+
# INSERT IGNORE INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
257
|
+
#
|
258
|
+
def insert_ignore
|
259
|
+
clone(:insert_ignore=>true)
|
260
|
+
end
|
261
|
+
|
262
|
+
# Sets up multi_insert or import to use ON DUPLICATE KEY UPDATE
|
263
|
+
# If you pass no arguments, ALL fields will be
|
264
|
+
# updated with the new values. If you pass the fields you
|
265
|
+
# want then ONLY those field will be updated.
|
266
|
+
#
|
267
|
+
# Useful if you have a unique key and want to update
|
268
|
+
# inserting rows that violate the unique key restriction.
|
269
|
+
#
|
270
|
+
# Examples:
|
271
|
+
#
|
272
|
+
# dataset.on_duplicate_key_update.multi_insert(
|
273
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
274
|
+
# )
|
275
|
+
#
|
276
|
+
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
277
|
+
# ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)
|
278
|
+
#
|
279
|
+
# dataset.on_duplicate_key_update(:value).multi_insert(
|
280
|
+
# [{:name => 'a', :value => 1}, {:name => 'b', :value => 2}]
|
281
|
+
# )
|
282
|
+
#
|
283
|
+
# INSERT INTO tablename (name, value) VALUES (a, 1), (b, 2)
|
284
|
+
# ON DUPLICATE KEY UPDATE value=VALUES(value)
|
285
|
+
#
|
286
|
+
def on_duplicate_key_update(*args)
|
287
|
+
clone(:on_duplicate_key_update => args)
|
288
|
+
end
|
289
|
+
|
236
290
|
# MySQL specific syntax for inserting multiple values at once.
|
237
291
|
def multi_insert_sql(columns, values)
|
292
|
+
if update_cols = opts[:on_duplicate_key_update]
|
293
|
+
update_cols = columns if update_cols.empty?
|
294
|
+
update_string = update_cols.map{|c| "#{quote_identifier(c)}=VALUES(#{quote_identifier(c)})"}.join(COMMA_SEPARATOR)
|
295
|
+
end
|
238
296
|
values = values.map {|r| literal(Array(r))}.join(COMMA_SEPARATOR)
|
239
|
-
["INSERT INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}"]
|
297
|
+
["INSERT#{' IGNORE' if opts[:insert_ignore]} INTO #{source_list(@opts[:from])} (#{identifier_list(columns)}) VALUES #{values}#{" ON DUPLICATE KEY UPDATE #{update_string}" if update_string}"]
|
240
298
|
end
|
241
299
|
|
242
300
|
# MySQL uses the nonstandard ` (backtick) for quoting identifiers.
|
@@ -286,9 +344,14 @@ module Sequel
|
|
286
344
|
end
|
287
345
|
|
288
346
|
# MySQL supports ORDER and LIMIT clauses in UPDATE statements.
|
289
|
-
def update_sql(values, opts = nil)
|
290
|
-
|
291
|
-
|
347
|
+
def update_sql(values, opts = (defarg=true;nil))
|
348
|
+
if defarg
|
349
|
+
sql = super(values)
|
350
|
+
opts = @opts
|
351
|
+
else
|
352
|
+
sql = super
|
353
|
+
opts = opts ? @opts.merge(opts) : @opts
|
354
|
+
end
|
292
355
|
|
293
356
|
if order = opts[:order]
|
294
357
|
sql << " ORDER BY #{expression_list(order)}"
|
@@ -321,14 +384,6 @@ module Sequel
|
|
321
384
|
def literal_true
|
322
385
|
BOOL_TRUE
|
323
386
|
end
|
324
|
-
|
325
|
-
# MySQL doesn't support DISTINCT ON
|
326
|
-
def select_distinct_sql(sql, opts)
|
327
|
-
if opts[:distinct]
|
328
|
-
raise(Error, "DISTINCT ON not supported by MySQL") unless opts[:distinct].empty?
|
329
|
-
sql << " DISTINCT"
|
330
|
-
end
|
331
|
-
end
|
332
387
|
end
|
333
388
|
end
|
334
389
|
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
require 'sequel_core/adapters/utils/unsupported'
|
1
|
+
Sequel.require %w'date_format unsupported', 'adapters/utils'
|
3
2
|
|
4
3
|
module Sequel
|
5
4
|
module Oracle
|
@@ -20,6 +19,12 @@ module Sequel
|
|
20
19
|
|
21
20
|
SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
|
22
21
|
|
22
|
+
# Oracle doesn't support DISTINCT ON
|
23
|
+
def distinct(*columns)
|
24
|
+
raise(Error, "DISTINCT ON not supported by Oracle") unless columns.empty?
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
23
28
|
# Oracle uses MINUS instead of EXCEPT, and doesn't support EXCEPT ALL
|
24
29
|
def except(dataset, all = false)
|
25
30
|
raise(Sequel::Error, "EXCEPT ALL not supported") if all
|
@@ -42,14 +47,6 @@ module Sequel
|
|
42
47
|
SELECT_CLAUSE_ORDER
|
43
48
|
end
|
44
49
|
|
45
|
-
# Oracle doesn't support DISTINCT ON
|
46
|
-
def select_distinct_sql(sql, opts)
|
47
|
-
if opts[:distinct]
|
48
|
-
raise(Error, "DISTINCT ON not supported by Oracle") unless opts[:distinct].empty?
|
49
|
-
sql << " DISTINCT"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
50
|
# Oracle requires a subselect to do limit and offset
|
54
51
|
def select_limit_sql(sql, opts)
|
55
52
|
if limit = opts[:limit]
|
@@ -35,18 +35,20 @@ module Sequel
|
|
35
35
|
@client_min_messages = :warning
|
36
36
|
@force_standard_strings = true
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
class << self
|
39
|
+
# By default, Sequel sets the minimum level of log messages sent to the client
|
40
|
+
# to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
|
41
|
+
# of mostly useless messages when running migrations, such as a couple of lines
|
42
|
+
# for every serial primary key field.
|
43
|
+
attr_accessor :client_min_messages
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
# By default, Sequel forces the use of standard strings, so that
|
46
|
+
# '\\' is interpreted as \\ and not \. While PostgreSQL defaults
|
47
|
+
# to interpreting plain strings as extended strings, this will change
|
48
|
+
# in a future version of PostgreSQL. Sequel assumes that SQL standard
|
49
|
+
# strings will be used.
|
50
|
+
attr_accessor :force_standard_strings
|
51
|
+
end
|
50
52
|
|
51
53
|
# Methods shared by adapter/connection instances.
|
52
54
|
module AdapterMethods
|
@@ -170,14 +172,9 @@ module Sequel
|
|
170
172
|
SQL_ROLLBACK = 'ROLLBACK'.freeze
|
171
173
|
SQL_RELEASE_SAVEPOINT = 'RELEASE SAVEPOINT autopoint_%d'.freeze
|
172
174
|
SYSTEM_TABLE_REGEXP = /^pg|sql/.freeze
|
173
|
-
TYPES = Sequel::
|
175
|
+
TYPES = Sequel::Database::TYPES.merge(File=>'bytea', String=>'text')
|
174
176
|
|
175
|
-
# Creates the function in the database.
|
176
|
-
def create_function(*args)
|
177
|
-
self << create_function_sql(*args)
|
178
|
-
end
|
179
|
-
|
180
|
-
# SQL statement to create database function. Arguments:
|
177
|
+
# Creates the function in the database. Arguments:
|
181
178
|
# * name : name of the function to create
|
182
179
|
# * definition : string definition of the function, or object file for a dynamically loaded C function.
|
183
180
|
# * opts : options hash:
|
@@ -197,47 +194,21 @@ module Sequel
|
|
197
194
|
# * :set : Configuration variables to set while the function is being run, can be a hash or an array of two pairs. search_path is
|
198
195
|
# often used here if :security_definer is used.
|
199
196
|
# * :strict : Makes the function return NULL when any argument is NULL.
|
200
|
-
def
|
201
|
-
|
202
|
-
if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
|
203
|
-
returns = opts[:returns] || 'void'
|
204
|
-
end
|
205
|
-
language = opts[:language] || 'SQL'
|
206
|
-
<<-END
|
207
|
-
CREATE#{' OR REPLACE' if opts[:replace]} FUNCTION #{name}#{sql_function_args(args)}
|
208
|
-
#{"RETURNS #{returns}" if returns}
|
209
|
-
LANGUAGE #{language}
|
210
|
-
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
211
|
-
#{'STRICT' if opts[:strict]}
|
212
|
-
#{'SECURITY DEFINER' if opts[:security_definer]}
|
213
|
-
#{"COST #{opts[:cost]}" if opts[:cost]}
|
214
|
-
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
215
|
-
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
216
|
-
AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
|
217
|
-
END
|
218
|
-
end
|
219
|
-
|
220
|
-
# Create the procedural language in the database. See create_language_sql for arguments.
|
221
|
-
def create_language(*args)
|
222
|
-
self << create_language_sql(*args)
|
197
|
+
def create_function(name, definition, opts={})
|
198
|
+
self << create_function_sql(name, definition, opts)
|
223
199
|
end
|
224
200
|
|
225
|
-
#
|
201
|
+
# Create the procedural language in the database. Arguments:
|
226
202
|
# * name : Name of the procedural language (e.g. plpgsql)
|
227
203
|
# * opts : options hash:
|
228
204
|
# * :handler : The name of a previously registered function used as a call handler for this language.
|
229
205
|
# * :trusted : Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
|
230
206
|
# * :validator : The name of previously registered function used as a validator of functions defined in this language.
|
231
|
-
def
|
232
|
-
|
207
|
+
def create_language(name, opts={})
|
208
|
+
self << create_language_sql(name, opts)
|
233
209
|
end
|
234
210
|
|
235
|
-
# Create a trigger in the database.
|
236
|
-
def create_trigger(*args)
|
237
|
-
self << create_trigger_sql(*args)
|
238
|
-
end
|
239
|
-
|
240
|
-
# SQL for creating a database trigger. Arguments:
|
211
|
+
# Create a trigger in the database. Arguments:
|
241
212
|
# * table : the table on which this trigger operates
|
242
213
|
# * name : the name of this trigger
|
243
214
|
# * function : the function to call for this trigger, which should return type trigger.
|
@@ -247,41 +218,29 @@ module Sequel
|
|
247
218
|
# * :each_row : Calls the trigger for each row instead of for each statement.
|
248
219
|
# * :events : Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
|
249
220
|
# the trigger is called for insert, update, or delete.
|
250
|
-
def
|
251
|
-
|
252
|
-
whence = opts[:after] ? 'AFTER' : 'BEFORE'
|
253
|
-
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
254
|
-
end
|
255
|
-
|
256
|
-
# Drops the function from the database. See drop_function_sql for arguments.
|
257
|
-
def drop_function(*args)
|
258
|
-
self << drop_function_sql(*args)
|
221
|
+
def create_trigger(table, name, function, opts={})
|
222
|
+
self << create_trigger_sql(table, name, function, opts)
|
259
223
|
end
|
260
224
|
|
261
|
-
#
|
225
|
+
# Drops the function from the database. Arguments:
|
262
226
|
# * name : name of the function to drop
|
263
227
|
# * opts : options hash:
|
264
228
|
# * :args : The arguments for the function. See create_function_sql.
|
265
229
|
# * :cascade : Drop other objects depending on this function.
|
266
230
|
# * :if_exists : Don't raise an error if the function doesn't exist.
|
267
|
-
def
|
268
|
-
|
231
|
+
def drop_function(name, opts={})
|
232
|
+
self << drop_function_sql(name, opts)
|
269
233
|
end
|
270
234
|
|
271
|
-
# Drops a procedural language from the database.
|
272
|
-
def drop_language(*args)
|
273
|
-
self << drop_language_sql(*args)
|
274
|
-
end
|
275
|
-
|
276
|
-
# SQL for dropping a procedural language from the database. Arguments:
|
235
|
+
# Drops a procedural language from the database. Arguments:
|
277
236
|
# * name : name of the procedural language to drop
|
278
237
|
# * opts : options hash:
|
279
238
|
# * :cascade : Drop other objects depending on this function.
|
280
239
|
# * :if_exists : Don't raise an error if the function doesn't exist.
|
281
|
-
def
|
282
|
-
|
240
|
+
def drop_language(name, opts={})
|
241
|
+
self << drop_language_sql(name, opts)
|
283
242
|
end
|
284
|
-
|
243
|
+
|
285
244
|
# Remove the cached entries for primary keys and sequences when dropping a table.
|
286
245
|
def drop_table(*names)
|
287
246
|
names.each do |name|
|
@@ -292,43 +251,14 @@ module Sequel
|
|
292
251
|
super
|
293
252
|
end
|
294
253
|
|
295
|
-
#
|
296
|
-
def drop_table_sql(name)
|
297
|
-
"DROP TABLE #{quote_schema_table(name)} CASCADE"
|
298
|
-
end
|
299
|
-
|
300
|
-
# Drops a trigger from the database. See drop_trigger_sql for arguments.
|
301
|
-
def drop_trigger(*args)
|
302
|
-
self << drop_trigger_sql(*args)
|
303
|
-
end
|
304
|
-
|
305
|
-
# SQL for dropping a trigger from the database. Arguments:
|
254
|
+
# Drops a trigger from the database. Arguments:
|
306
255
|
# * table : table from which to drop the trigger
|
307
256
|
# * name : name of the trigger to drop
|
308
257
|
# * opts : options hash:
|
309
258
|
# * :cascade : Drop other objects depending on this function.
|
310
259
|
# * :if_exists : Don't raise an error if the function doesn't exist.
|
311
|
-
def
|
312
|
-
|
313
|
-
end
|
314
|
-
|
315
|
-
# PostgreSQL specific index SQL.
|
316
|
-
def index_definition_sql(table_name, index)
|
317
|
-
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
318
|
-
expr = literal(Array(index[:columns]))
|
319
|
-
unique = "UNIQUE " if index[:unique]
|
320
|
-
index_type = index[:type]
|
321
|
-
filter = index[:where] || index[:filter]
|
322
|
-
filter = " WHERE #{filter_expr(filter)}" if filter
|
323
|
-
case index_type
|
324
|
-
when :full_text
|
325
|
-
cols = Array(index[:columns]).map{|x| SQL::Function.new(:COALESCE, x, '')}.sql_string_join(' ')
|
326
|
-
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{literal(cols)}))"
|
327
|
-
index_type = :gin
|
328
|
-
when :spatial
|
329
|
-
index_type = :gist
|
330
|
-
end
|
331
|
-
"CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
260
|
+
def drop_trigger(table, name, opts={})
|
261
|
+
self << drop_trigger_sql(table, name, opts)
|
332
262
|
end
|
333
263
|
|
334
264
|
# Dataset containing all current database locks
|
@@ -358,12 +288,6 @@ module Sequel
|
|
358
288
|
end
|
359
289
|
end
|
360
290
|
|
361
|
-
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
362
|
-
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
363
|
-
def rename_table_sql(name, new_name)
|
364
|
-
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
365
|
-
end
|
366
|
-
|
367
291
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
368
292
|
# managing incrementing primary keys.
|
369
293
|
def serial_primary_key_options
|
@@ -402,7 +326,7 @@ module Sequel
|
|
402
326
|
# * :schema - The schema to search (default_schema by default)
|
403
327
|
# * :server - The server to use
|
404
328
|
def tables(opts={})
|
405
|
-
ds = self[:pg_class].filter(:relkind=>'r').select(:relname).exclude(
|
329
|
+
ds = self[:pg_class].filter(:relkind=>'r').select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server])
|
406
330
|
ds.join!(:pg_namespace, :oid=>:relnamespace, :nspname=>(opts[:schema]||default_schema).to_s) if opts[:schema] || default_schema
|
407
331
|
ds.identifier_input_method = nil
|
408
332
|
ds.identifier_output_method = nil
|
@@ -411,9 +335,16 @@ module Sequel
|
|
411
335
|
end
|
412
336
|
|
413
337
|
# PostgreSQL supports multi-level transactions using save points.
|
414
|
-
|
415
|
-
|
416
|
-
|
338
|
+
# To use a savepoint instead of reusing the current transaction,
|
339
|
+
# use the :savepoint=>true option.
|
340
|
+
def transaction(opts={})
|
341
|
+
unless opts.is_a?(Hash)
|
342
|
+
Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
|
343
|
+
opts = {:server=>opts}
|
344
|
+
end
|
345
|
+
synchronize(opts[:server]) do |conn|
|
346
|
+
return yield(conn) if @transactions.include?(Thread.current) and !opts[:savepoint]
|
347
|
+
conn.transaction_depth ||= 0
|
417
348
|
if conn.transaction_depth > 0
|
418
349
|
log_info(SQL_SAVEPOINT % conn.transaction_depth)
|
419
350
|
conn.execute(SQL_SAVEPOINT % conn.transaction_depth)
|
@@ -423,6 +354,7 @@ module Sequel
|
|
423
354
|
end
|
424
355
|
begin
|
425
356
|
conn.transaction_depth += 1
|
357
|
+
@transactions << Thread.current
|
426
358
|
yield conn
|
427
359
|
rescue ::Exception => e
|
428
360
|
if conn.transaction_depth > 1
|
@@ -431,17 +363,19 @@ module Sequel
|
|
431
363
|
else
|
432
364
|
log_info(SQL_ROLLBACK)
|
433
365
|
conn.execute(SQL_ROLLBACK) rescue nil
|
366
|
+
@transactions.delete(Thread.current)
|
434
367
|
end
|
435
368
|
transaction_error(e, *CONVERTED_EXCEPTIONS)
|
436
369
|
ensure
|
437
370
|
unless e
|
438
371
|
begin
|
439
|
-
if conn.transaction_depth
|
440
|
-
log_info(SQL_COMMIT)
|
441
|
-
conn.execute(SQL_COMMIT)
|
442
|
-
else
|
372
|
+
if conn.transaction_depth > 1
|
443
373
|
log_info(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
444
374
|
conn.execute(SQL_RELEASE_SAVEPOINT % [conn.transaction_depth - 1])
|
375
|
+
else
|
376
|
+
log_info(SQL_COMMIT)
|
377
|
+
conn.execute(SQL_COMMIT)
|
378
|
+
@transactions.delete(Thread.current)
|
445
379
|
end
|
446
380
|
rescue => e
|
447
381
|
log_info(e.message)
|
@@ -455,6 +389,59 @@ module Sequel
|
|
455
389
|
|
456
390
|
private
|
457
391
|
|
392
|
+
# SQL statement to create database function.
|
393
|
+
def create_function_sql(name, definition, opts={})
|
394
|
+
args = opts[:args]
|
395
|
+
if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
|
396
|
+
returns = opts[:returns] || 'void'
|
397
|
+
end
|
398
|
+
language = opts[:language] || 'SQL'
|
399
|
+
<<-END
|
400
|
+
CREATE#{' OR REPLACE' if opts[:replace]} FUNCTION #{name}#{sql_function_args(args)}
|
401
|
+
#{"RETURNS #{returns}" if returns}
|
402
|
+
LANGUAGE #{language}
|
403
|
+
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
404
|
+
#{'STRICT' if opts[:strict]}
|
405
|
+
#{'SECURITY DEFINER' if opts[:security_definer]}
|
406
|
+
#{"COST #{opts[:cost]}" if opts[:cost]}
|
407
|
+
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
408
|
+
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
409
|
+
AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
|
410
|
+
END
|
411
|
+
end
|
412
|
+
|
413
|
+
# SQL for creating a procedural language.
|
414
|
+
def create_language_sql(name, opts={})
|
415
|
+
"CREATE#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
|
416
|
+
end
|
417
|
+
|
418
|
+
# SQL for creating a database trigger.
|
419
|
+
def create_trigger_sql(table, name, function, opts={})
|
420
|
+
events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
|
421
|
+
whence = opts[:after] ? 'AFTER' : 'BEFORE'
|
422
|
+
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
423
|
+
end
|
424
|
+
|
425
|
+
# SQL for dropping a function from the database.
|
426
|
+
def drop_function_sql(name, opts={})
|
427
|
+
"DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
|
428
|
+
end
|
429
|
+
|
430
|
+
# SQL for dropping a procedural language from the database.
|
431
|
+
def drop_language_sql(name, opts={})
|
432
|
+
"DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
|
433
|
+
end
|
434
|
+
|
435
|
+
# Always CASCADE the table drop
|
436
|
+
def drop_table_sql(name)
|
437
|
+
"DROP TABLE #{quote_schema_table(name)} CASCADE"
|
438
|
+
end
|
439
|
+
|
440
|
+
# SQL for dropping a trigger from the database.
|
441
|
+
def drop_trigger_sql(table, name, opts={})
|
442
|
+
"DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
|
443
|
+
end
|
444
|
+
|
458
445
|
# PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
|
459
446
|
def identifier_input_method_default
|
460
447
|
nil
|
@@ -465,6 +452,25 @@ module Sequel
|
|
465
452
|
nil
|
466
453
|
end
|
467
454
|
|
455
|
+
# PostgreSQL specific index SQL.
|
456
|
+
def index_definition_sql(table_name, index)
|
457
|
+
cols = index[:columns]
|
458
|
+
index_name = index[:name] || default_index_name(table_name, cols)
|
459
|
+
expr = literal(Array(cols))
|
460
|
+
unique = "UNIQUE " if index[:unique]
|
461
|
+
index_type = index[:type]
|
462
|
+
filter = index[:where] || index[:filter]
|
463
|
+
filter = " WHERE #{filter_expr(filter)}" if filter
|
464
|
+
case index_type
|
465
|
+
when :full_text
|
466
|
+
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}, #{dataset.send(:full_text_string_join, cols)}))"
|
467
|
+
index_type = :gin
|
468
|
+
when :spatial
|
469
|
+
index_type = :gist
|
470
|
+
end
|
471
|
+
"CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
|
472
|
+
end
|
473
|
+
|
468
474
|
# The result of the insert for the given table and values. If values
|
469
475
|
# is an array, assume the first column is the primary key and return
|
470
476
|
# that. If values is a hash, lookup the primary key for the table. If
|
@@ -499,6 +505,12 @@ module Sequel
|
|
499
505
|
PREPARED_ARG_PLACEHOLDER
|
500
506
|
end
|
501
507
|
|
508
|
+
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
509
|
+
# a rename table operation, so speciying a new schema in new_name will not have an effect.
|
510
|
+
def rename_table_sql(name, new_name)
|
511
|
+
"ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
|
512
|
+
end
|
513
|
+
|
502
514
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
503
515
|
def schema_parse_table(table_name, opts)
|
504
516
|
ds2 = dataset
|
@@ -506,7 +518,7 @@ module Sequel
|
|
506
518
|
SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
|
507
519
|
SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
|
508
520
|
SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
|
509
|
-
SQL::Function.new(:COALESCE,
|
521
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
|
510
522
|
from(:pg_class).
|
511
523
|
join(:pg_attribute, :attrelid=>:oid).
|
512
524
|
join(:pg_type, :oid=>:atttypid).
|
@@ -520,7 +532,7 @@ module Sequel
|
|
520
532
|
ds.identifier_input_method = nil
|
521
533
|
ds.identifier_output_method = nil
|
522
534
|
ds.map do |row|
|
523
|
-
row[:default] = nil if row[:default]
|
535
|
+
row[:default] = nil if blank_object?(row[:default])
|
524
536
|
row[:type] = schema_column_type(row[:db_type])
|
525
537
|
[ds2.send(:output_identifier, row.delete(:name)), row]
|
526
538
|
end
|
@@ -566,7 +578,7 @@ module Sequel
|
|
566
578
|
def prepared_sql
|
567
579
|
return @prepared_sql if @prepared_sql
|
568
580
|
super
|
569
|
-
if @prepared_type == :insert and server_version >= 80200
|
581
|
+
if @prepared_type == :insert and !@opts[:disable_insert_returning] and server_version >= 80200
|
570
582
|
@prepared_sql = insert_returning_pk_sql(@prepared_modify_values)
|
571
583
|
meta_def(:insert_returning_pk_sql){|*args| prepared_sql}
|
572
584
|
end
|
@@ -574,6 +586,16 @@ module Sequel
|
|
574
586
|
end
|
575
587
|
end
|
576
588
|
|
589
|
+
# Add the disable_insert_returning! mutation method
|
590
|
+
def self.extended(obj)
|
591
|
+
obj.def_mutation_method(:disable_insert_returning)
|
592
|
+
end
|
593
|
+
|
594
|
+
# Add the disable_insert_returning! mutation method
|
595
|
+
def self.included(mod)
|
596
|
+
mod.def_mutation_method(:disable_insert_returning)
|
597
|
+
end
|
598
|
+
|
577
599
|
# Return the results of an ANALYZE query as a string
|
578
600
|
def analyze(opts = nil)
|
579
601
|
analysis = []
|
@@ -583,6 +605,11 @@ module Sequel
|
|
583
605
|
analysis.join("\r\n")
|
584
606
|
end
|
585
607
|
|
608
|
+
# Disable the use of INSERT RETURNING, even if the server supports it
|
609
|
+
def disable_insert_returning
|
610
|
+
clone(:disable_insert_returning=>true)
|
611
|
+
end
|
612
|
+
|
586
613
|
# Return the results of an EXPLAIN query as a string
|
587
614
|
def explain(opts = nil)
|
588
615
|
analysis = []
|
@@ -606,14 +633,13 @@ module Sequel
|
|
606
633
|
# in 8.3 by default, and available for earlier versions as an add-on).
|
607
634
|
def full_text_search(cols, terms, opts = {})
|
608
635
|
lang = opts[:language] || 'simple'
|
609
|
-
|
610
|
-
filter("to_tsvector(#{literal(lang)}, #{literal(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
|
636
|
+
filter("to_tsvector(#{literal(lang)}, #{full_text_string_join(cols)}) @@ to_tsquery(#{literal(lang)}, #{literal(Array(terms).join(' | '))})")
|
611
637
|
end
|
612
638
|
|
613
639
|
# Insert given values into the database.
|
614
640
|
def insert(*values)
|
615
|
-
if !@opts[:sql] and server_version >= 80200
|
616
|
-
|
641
|
+
if !@opts[:sql] and !@opts[:disable_insert_returning] and server_version >= 80200
|
642
|
+
clone(default_server_opts(:sql=>insert_returning_pk_sql(*values))).single_value
|
617
643
|
else
|
618
644
|
execute_insert(insert_sql(*values), :table=>opts[:from].first,
|
619
645
|
:values=>values.size == 1 ? values.first : values)
|
@@ -627,7 +653,7 @@ module Sequel
|
|
627
653
|
|
628
654
|
# Insert a record returning the record inserted
|
629
655
|
def insert_select(*values)
|
630
|
-
|
656
|
+
naked.clone(default_server_opts(:sql=>insert_returning_sql(nil, *values))).single_record if server_version >= 80200
|
631
657
|
end
|
632
658
|
|
633
659
|
# Locks the table with the specified mode.
|
@@ -712,6 +738,14 @@ module Sequel
|
|
712
738
|
def server_version
|
713
739
|
db.server_version(@opts[:server])
|
714
740
|
end
|
741
|
+
|
742
|
+
# Concatenate the expressions with a space in between
|
743
|
+
def full_text_string_join(cols)
|
744
|
+
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
|
745
|
+
cols = cols.zip([' '] * cols.length).flatten
|
746
|
+
cols.pop
|
747
|
+
literal(SQL::StringExpression.new(:'||', *cols))
|
748
|
+
end
|
715
749
|
end
|
716
750
|
end
|
717
751
|
end
|