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.
Files changed (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -1,4 +1,4 @@
1
- require 'sequel_core/adapters/utils/unsupported'
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 'sequel_core/adapters/utils/unsupported'
1
+ Sequel.require 'adapters/utils/unsupported'
2
2
 
3
3
  module Sequel
4
- module Schema
5
- module SQL
6
- # Keep default column_references_sql for add_foreign_key support
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
- # Set the default options used for CREATE TABLE
12
- metaattr_accessor :default_charset, :default_collate, :default_engine
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::Schema::SQL::NOT_NULL
19
- NULL = Sequel::Schema::SQL::NULL
20
- PRIMARY_KEY = Sequel::Schema::SQL::PRIMARY_KEY
21
- TYPES = Sequel::Schema::SQL::TYPES.merge(DateTime=>'datetime', \
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::Schema::SQL::UNIQUE
24
- UNSIGNED = Sequel::Schema::SQL::UNSIGNED
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].blank?
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
- sql = super
180
- opts = opts ? @opts.merge(opts) : @opts
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.blank?
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
- @opts[:having] = {}
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
- sql = super
291
- opts = opts ? @opts.merge(opts) : @opts
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 'sequel_core/adapters/utils/date_format'
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
- # By default, Sequel sets the minimum level of log messages sent to the client
39
- # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
40
- # of mostly useless messages when running migrations, such as a couple of lines
41
- # for every serial primary key field.
42
- metaattr_accessor :client_min_messages
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
- # By default, Sequel forces the use of standard strings, so that
45
- # '\\' is interpreted as \\ and not \. While PostgreSQL defaults
46
- # to interpreting plain strings as extended strings, this will change
47
- # in a future version of PostgreSQL. Sequel assumes that SQL standard
48
- # strings will be used.
49
- metaattr_accessor :force_standard_strings
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::Schema::SQL::TYPES.merge(File=>'bytea', String=>'text')
175
+ TYPES = Sequel::Database::TYPES.merge(File=>'bytea', String=>'text')
174
176
 
175
- # Creates the function in the database. See create_function_sql for arguments.
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 create_function_sql(name, definition, opts={})
201
- args = opts[:args]
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
- # SQL for creating a procedural language. Arguments:
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 create_language_sql(name, opts={})
232
- "CREATE#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
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. See create_trigger_sql for arguments.
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 create_trigger_sql(table, name, function, opts={})
251
- events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
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
- # SQL for dropping a function from the database. Arguments:
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 drop_function_sql(name, opts={})
268
- "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
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. See drop_language_sql for arguments.
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 drop_language_sql(name, opts={})
282
- "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
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
- # Always CASCADE the table drop
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 drop_trigger_sql(table, name, opts={})
312
- "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
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(:relname.like(SYSTEM_TABLE_REGEXP)).server(opts[:server])
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
- def transaction(server=nil)
415
- synchronize(server) do |conn|
416
- conn.transaction_depth = 0 if conn.transaction_depth.nil?
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 < 2
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, {:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)}.sql_expr, false).as(:primary_key)).
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].blank?
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
- cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}.sql_string_join(' ')
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
- single_value(default_server_opts(:sql=>insert_returning_pk_sql(*values)))
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
- single_record(default_server_opts(:naked=>true, :sql=>insert_returning_sql(nil, *values))) if server_version >= 80200
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