sequel 2.11.0 → 2.12.0

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