sequel 2.11.0 → 2.12.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 +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
|