sequel 3.32.0 → 3.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +42 -0
  2. data/Rakefile +3 -2
  3. data/doc/migration.rdoc +41 -14
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.29.0.txt +1 -1
  6. data/doc/release_notes/3.33.0.txt +157 -0
  7. data/doc/sharding.rdoc +93 -7
  8. data/doc/testing.rdoc +1 -1
  9. data/lib/sequel/adapters/do.rb +1 -0
  10. data/lib/sequel/adapters/do/mysql.rb +6 -0
  11. data/lib/sequel/adapters/jdbc.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  13. data/lib/sequel/adapters/mock.rb +10 -5
  14. data/lib/sequel/adapters/mysql.rb +23 -1
  15. data/lib/sequel/adapters/mysql2.rb +16 -2
  16. data/lib/sequel/adapters/postgres.rb +22 -4
  17. data/lib/sequel/adapters/shared/mysql.rb +51 -10
  18. data/lib/sequel/adapters/shared/postgres.rb +101 -63
  19. data/lib/sequel/adapters/shared/sqlite.rb +19 -0
  20. data/lib/sequel/adapters/sqlite.rb +71 -16
  21. data/lib/sequel/adapters/swift.rb +1 -0
  22. data/lib/sequel/connection_pool.rb +1 -1
  23. data/lib/sequel/connection_pool/sharded_single.rb +6 -1
  24. data/lib/sequel/connection_pool/sharded_threaded.rb +6 -1
  25. data/lib/sequel/connection_pool/threaded.rb +12 -11
  26. data/lib/sequel/database/connecting.rb +2 -0
  27. data/lib/sequel/database/misc.rb +6 -0
  28. data/lib/sequel/database/query.rb +1 -1
  29. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  30. data/lib/sequel/extensions/migration.rb +45 -7
  31. data/lib/sequel/extensions/server_block.rb +139 -0
  32. data/lib/sequel/model/associations.rb +9 -9
  33. data/lib/sequel/model/inflections.rb +1 -1
  34. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  35. data/lib/sequel/plugins/json_serializer.rb +1 -1
  36. data/lib/sequel/plugins/list.rb +12 -2
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mysql_spec.rb +64 -8
  39. data/spec/adapters/postgres_spec.rb +139 -78
  40. data/spec/adapters/sqlite_spec.rb +87 -0
  41. data/spec/core/connection_pool_spec.rb +14 -0
  42. data/spec/core/database_spec.rb +5 -0
  43. data/spec/core/mock_adapter_spec.rb +21 -9
  44. data/spec/extensions/arbitrary_servers_spec.rb +114 -0
  45. data/spec/extensions/instance_hooks_spec.rb +19 -0
  46. data/spec/extensions/list_spec.rb +31 -0
  47. data/spec/extensions/migration_spec.rb +61 -4
  48. data/spec/extensions/server_block_spec.rb +90 -0
  49. data/spec/extensions/spec_helper.rb +1 -1
  50. data/spec/files/transaction_migrations/001_create_alt_basic.rb +3 -0
  51. data/spec/files/transaction_migrations/002_create_basic.rb +3 -0
  52. data/spec/files/transactionless_migrations/001_create_alt_basic.rb +4 -0
  53. data/spec/files/transactionless_migrations/002_create_basic.rb +4 -0
  54. data/spec/integration/dataset_test.rb +2 -2
  55. data/spec/integration/plugin_test.rb +9 -9
  56. data/spec/integration/schema_test.rb +3 -1
  57. data/spec/integration/transaction_test.rb +2 -2
  58. metadata +12 -2
data/doc/testing.rdoc CHANGED
@@ -106,7 +106,7 @@ These specs are broken down into two parts. For each database, there are specif
106
106
 
107
107
  == Environment variables
108
108
 
109
- Sequel often uses environment variables when testing to specify either the database to be tested or specify how testing should be done. You can also specify the databases to test by copying spec/spec_config.rb.example to spec/spec_config.rb and modifying it. See that file for details. It may be necessary to use spec_config.rb as opposed to an environment variable if you database connection cannot be specified by a connection string.
109
+ Sequel often uses environment variables when testing to specify either the database to be tested or specify how testing should be done. You can also specify the databases to test by copying spec/spec_config.rb.example to spec/spec_config.rb and modifying it. See that file for details. It may be necessary to use spec_config.rb as opposed to an environment variable if your database connection cannot be specified by a connection string.
110
110
 
111
111
  === Connection Strings
112
112
 
@@ -30,6 +30,7 @@ module Sequel
30
30
  Sequel.ts_require 'adapters/do/sqlite'
31
31
  db.extend(Sequel::DataObjects::SQLite::DatabaseMethods)
32
32
  db.dataset_class = Sequel::DataObjects::SQLite::Dataset
33
+ db.set_integer_booleans
33
34
  end
34
35
  }
35
36
 
@@ -30,6 +30,12 @@ module Sequel
30
30
  APOS_RE = Dataset::APOS_RE
31
31
  DOUBLE_APOS = Dataset::DOUBLE_APOS
32
32
 
33
+ # The DataObjects MySQL driver uses the number of rows actually modified in the update,
34
+ # instead of the number of matched by the filter.
35
+ def provides_accurate_rows_matched?
36
+ false
37
+ end
38
+
33
39
  # Use execute_insert to execute the replace_sql.
34
40
  def replace(*args)
35
41
  execute_insert(replace_sql(*args))
@@ -47,6 +47,7 @@ module Sequel
47
47
  Sequel.ts_require 'adapters/jdbc/sqlite'
48
48
  db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
49
49
  db.dataset_class = Sequel::JDBC::SQLite::Dataset
50
+ db.set_integer_booleans
50
51
  JDBC.load_gem('sqlite3')
51
52
  org.sqlite.JDBC
52
53
  end,
@@ -71,11 +71,6 @@ module Sequel
71
71
  def replace(*args)
72
72
  execute_insert(replace_sql(*args))
73
73
  end
74
-
75
- # MySQL on JDBC does provides an accurate number of rows matched.
76
- def provides_accurate_rows_matched?
77
- true
78
- end
79
74
  end
80
75
  end
81
76
  end
@@ -110,6 +110,7 @@ module Sequel
110
110
  super
111
111
  opts = @opts
112
112
  if mod_name = SHARED_ADAPTERS[opts[:host]]
113
+ @shared_adapter = true
113
114
  require "sequel/adapters/shared/#{opts[:host]}"
114
115
  extend Sequel.const_get(mod_name)::DatabaseMethods
115
116
  extend_datasets Sequel.const_get(mod_name)::DatasetMethods
@@ -155,7 +156,7 @@ module Sequel
155
156
 
156
157
  # Enable use of savepoints.
157
158
  def supports_savepoints?
158
- true
159
+ shared_adapter? ? super : true
159
160
  end
160
161
 
161
162
  private
@@ -177,7 +178,7 @@ module Sequel
177
178
  def _execute(c, sql, opts={}, &block)
178
179
  sql += " -- args: #{opts[:arguments].inspect}" if opts[:arguments]
179
180
  sql += " -- #{@opts[:append]}" if @opts[:append]
180
- sql += " -- #{c.server}" if c.server != :default
181
+ sql += " -- #{c.server.is_a?(Symbol) ? c.server : c.server.inspect}" if c.server != :default
181
182
  log_info(sql)
182
183
  @sqls << sql
183
184
 
@@ -281,15 +282,19 @@ module Sequel
281
282
  end
282
283
 
283
284
  def quote_identifiers_default
284
- false
285
+ shared_adapter? ? super : false
285
286
  end
286
287
 
287
288
  def identifier_input_method_default
288
- nil
289
+ shared_adapter? ? super : nil
289
290
  end
290
291
 
291
292
  def identifier_output_method_default
292
- nil
293
+ shared_adapter? ? super : nil
294
+ end
295
+
296
+ def shared_adapter?
297
+ @shared_adapter
293
298
  end
294
299
  end
295
300
 
@@ -268,6 +268,10 @@ module Sequel
268
268
  include Sequel::MySQL::PreparedStatements::DatasetMethods
269
269
 
270
270
  Database::DatasetClass = self
271
+
272
+ # Regular expression used for getting accurate number of rows
273
+ # matched by an update statement.
274
+ AFFECTED_ROWS_RE = /Rows matched:\s+(\d+)\s+Changed:\s+\d+\s+Warnings:\s+\d+/.freeze
271
275
 
272
276
  # Delete rows matching this dataset
273
277
  def delete
@@ -311,6 +315,12 @@ module Sequel
311
315
  execute_dui(insert_sql(*values)){|c| return c.insert_id}
312
316
  end
313
317
 
318
+ # You can parse out the correct number of rows matched using the query info,
319
+ # even though affected_rows doesn't provide an accurate number.
320
+ def provides_accurate_rows_matched?
321
+ true
322
+ end
323
+
314
324
  # Replace (update or insert) the matching row.
315
325
  def replace(*args)
316
326
  execute_dui(replace_sql(*args)){|c| return c.insert_id}
@@ -334,11 +344,23 @@ module Sequel
334
344
 
335
345
  # Update the matching rows.
336
346
  def update(values={})
337
- execute_dui(update_sql(values)){|c| return c.affected_rows}
347
+ execute_dui(update_sql(values)){|c| return affected_rows(c)}
338
348
  end
339
349
 
340
350
  private
341
351
 
352
+ # Try to get an accurate number of rows matched using the query
353
+ # info. Fall back to affected_rows if there was no match, but
354
+ # that may be inaccurate.
355
+ def affected_rows(conn)
356
+ s = conn.info
357
+ if s && s =~ AFFECTED_ROWS_RE
358
+ $1.to_i
359
+ else
360
+ conn.affected_rows
361
+ end
362
+ end
363
+
342
364
  # Set the :type option to :select if it hasn't been set.
343
365
  def execute(sql, opts={}, &block)
344
366
  super(sql, {:type=>:select}.merge(opts), &block)
@@ -45,6 +45,7 @@ module Sequel
45
45
  opts = server_opts(server)
46
46
  opts[:host] ||= 'localhost'
47
47
  opts[:username] ||= opts[:user]
48
+ opts[:flags] = ::Mysql2::Client::FOUND_ROWS if ::Mysql2::Client.const_defined?(:FOUND_ROWS)
48
49
  conn = ::Mysql2::Client.new(opts)
49
50
 
50
51
  sqls = []
@@ -138,8 +139,21 @@ module Sequel
138
139
  # Yield all rows matching this dataset.
139
140
  def fetch_rows(sql, &block)
140
141
  execute(sql) do |r|
141
- @columns = r.fields
142
- r.each(:cast_booleans => db.convert_tinyint_to_bool, &block)
142
+ if identifier_output_method
143
+ cols = r.fields
144
+ @columns = cols2 = cols.map{|c| output_identifier(c.to_s)}
145
+ cs = cols.zip(cols2)
146
+ r.each(:cast_booleans => db.convert_tinyint_to_bool) do |row|
147
+ h = {}
148
+ cs.each do |a, b|
149
+ h[b] = row[a]
150
+ end
151
+ yield h
152
+ end
153
+ else
154
+ @columns = r.fields
155
+ r.each(:cast_booleans => db.convert_tinyint_to_bool, &block)
156
+ end
143
157
  end
144
158
  self
145
159
  end
@@ -88,11 +88,26 @@ module Sequel
88
88
  module Postgres
89
89
  CONVERTED_EXCEPTIONS << PGError
90
90
 
91
+ NAN = 0.0/0.0
92
+ PLUS_INFINITY = 1.0/0.0
93
+ MINUS_INFINITY = -1.0/0.0
94
+
91
95
  TYPE_TRANSLATOR = tt = Class.new do
92
96
  def boolean(s) s == 't' end
93
97
  def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
94
98
  def integer(s) s.to_i end
95
- def float(s) s.to_f end
99
+ def float(s)
100
+ case s
101
+ when 'NaN'
102
+ NAN
103
+ when 'Infinity'
104
+ PLUS_INFINITY
105
+ when '-Infinity'
106
+ MINUS_INFINITY
107
+ else
108
+ s.to_f
109
+ end
110
+ end
96
111
  def date(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
97
112
  end.new
98
113
 
@@ -215,18 +230,21 @@ module Sequel
215
230
 
216
231
  # Connects to the database. In addition to the standard database
217
232
  # options, using the :encoding or :charset option changes the
218
- # client encoding for the connection.
233
+ # client encoding for the connection, :connect_timeout is a
234
+ # connection timeout in seconds, and :sslmode sets whether postgres's
235
+ # sslmode. :connect_timeout and :ssl_mode are only supported if the pg
236
+ # driver is used.
219
237
  def connect(server)
220
238
  opts = server_opts(server)
221
239
  conn = if SEQUEL_POSTGRES_USES_PG
222
240
  connection_params = {
223
241
  :host => opts[:host],
224
242
  :port => opts[:port] || 5432,
225
- :tty => '',
226
243
  :dbname => opts[:database],
227
244
  :user => opts[:user],
228
245
  :password => opts[:password],
229
- :connect_timeout => opts[:connect_timeout] || 20
246
+ :connect_timeout => opts[:connect_timeout] || 20,
247
+ :sslmode => opts[:sslmode]
230
248
  }.delete_if { |key, value| blank_object?(value) }
231
249
  Adapter.connect(connection_params)
232
250
  else
@@ -31,7 +31,7 @@ module Sequel
31
31
  module DatabaseMethods
32
32
  AUTO_INCREMENT = 'AUTO_INCREMENT'.freeze
33
33
  CAST_TYPES = {String=>:CHAR, Integer=>:SIGNED, Time=>:DATETIME, DateTime=>:DATETIME, Numeric=>:DECIMAL, BigDecimal=>:DECIMAL, File=>:BINARY}
34
- COLUMN_DEFINITION_ORDER = [:null, :default, :unique, :primary_key, :auto_increment, :references]
34
+ COLUMN_DEFINITION_ORDER = [:collate, :null, :default, :unique, :primary_key, :auto_increment, :references]
35
35
  PRIMARY = 'PRIMARY'.freeze
36
36
 
37
37
  # MySQL's cast rules are restrictive in that you can't just cast to any possible
@@ -91,12 +91,18 @@ module Sequel
91
91
 
92
92
  # MySQL supports prepared transactions (two-phase commit) using XA
93
93
  def supports_prepared_transactions?
94
- true
94
+ server_version >= 50000
95
95
  end
96
96
 
97
97
  # MySQL supports savepoints
98
98
  def supports_savepoints?
99
- true
99
+ server_version >= 50000
100
+ end
101
+
102
+ # MySQL doesn't appear to support savepoints inside prepared transactions in >=5.5.12,
103
+ # see http://bugs.mysql.com/bug.php?id=64374
104
+ def supports_savepoints_in_prepared_transactions?
105
+ super && server_version <= 50512
100
106
  end
101
107
 
102
108
  # MySQL supports transaction isolation levels
@@ -139,6 +145,7 @@ module Sequel
139
145
  if related = op.delete(:table)
140
146
  sql = super(table, op)
141
147
  op[:table] = related
148
+ op[:key] ||= primary_key_from_schema(related)
142
149
  [sql, "ALTER TABLE #{quote_schema_table(table)} ADD FOREIGN KEY (#{quote_identifier(op[:name])})#{column_references_sql(op)}"]
143
150
  else
144
151
  super(table, op)
@@ -167,6 +174,11 @@ module Sequel
167
174
  raise(Error, "must specify constraint type via :type=>(:foreign_key|:primary_key|:unique) when dropping constraints on MySQL")
168
175
  end
169
176
  "ALTER TABLE #{quote_schema_table(table)} DROP #{type} #{quote_identifier(op[:name])}"
177
+ when :add_constraint
178
+ if op[:type] == :foreign_key
179
+ op[:key] ||= primary_key_from_schema(op[:table])
180
+ end
181
+ super(table, op)
170
182
  else
171
183
  super(table, op)
172
184
  end
@@ -222,11 +234,41 @@ module Sequel
222
234
  engine = options.fetch(:engine, Sequel::MySQL.default_engine)
223
235
  charset = options.fetch(:charset, Sequel::MySQL.default_charset)
224
236
  collate = options.fetch(:collate, Sequel::MySQL.default_collate)
237
+ generator.constraints.sort_by{|c| (c[:type] == :primary_key) ? -1 : 1}
238
+
239
+ key_proc = lambda do |t|
240
+ if t == name
241
+ if pk = generator.primary_key_name
242
+ [pk]
243
+ elsif !(pkc = generator.constraints.select{|con| con[:type] == :primary_key}).empty?
244
+ pkc.first[:columns]
245
+ end
246
+ else
247
+ primary_key_from_schema(t)
248
+ end
249
+ end
250
+
251
+ generator.constraints.each do |c|
252
+ if c[:type] == :foreign_key
253
+ c[:key] ||= key_proc.call(c[:table])
254
+ end
255
+ end
256
+
225
257
  generator.columns.each do |c|
226
258
  if t = c.delete(:table)
227
- generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key))
259
+ same_table = t == name
260
+ k = c[:key]
261
+
262
+ key ||= key_proc.call(t)
263
+
264
+ if same_table && !k.nil?
265
+ generator.constraints.unshift(:type=>:unique, :columns=>Array(k))
266
+ end
267
+
268
+ generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key, :key=>key))
228
269
  end
229
270
  end
271
+
230
272
  "#{super}#{" ENGINE=#{engine}" if engine}#{" DEFAULT CHARSET=#{charset}" if charset}#{" DEFAULT COLLATE=#{collate}" if collate}"
231
273
  end
232
274
 
@@ -261,6 +303,11 @@ module Sequel
261
303
  "CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
262
304
  end
263
305
 
306
+ # Parse the schema for the given table to get an array of primary key columns
307
+ def primary_key_from_schema(table)
308
+ schema(table).select{|a| a[1][:primary_key]}.map{|a| a[0]}
309
+ end
310
+
264
311
  # Rollback the currently open XA transaction
265
312
  def rollback_transaction(conn, opts={})
266
313
  if (s = opts[:prepare]) && @transactions[conn][:savepoint_level] <= 1
@@ -503,12 +550,6 @@ module Sequel
503
550
  [insert_sql(columns, sql)]
504
551
  end
505
552
 
506
- # MySQL uses the number of rows actually modified in the update,
507
- # instead of the number of matched by the filter.
508
- def provides_accurate_rows_matched?
509
- false
510
- end
511
-
512
553
  # MySQL uses the nonstandard ` (backtick) for quoting identifiers.
513
554
  def quoted_identifier_append(sql, c)
514
555
  sql << BACKTICK << c.to_s << BACKTICK
@@ -33,10 +33,10 @@ module Sequel
33
33
  # Array of exceptions that need to be converted. JDBC
34
34
  # uses NativeExceptions, the native adapter uses PGError.
35
35
  CONVERTED_EXCEPTIONS = []
36
-
36
+
37
37
  @client_min_messages = :warning
38
38
  @force_standard_strings = true
39
-
39
+
40
40
  class << self
41
41
  # By default, Sequel sets the minimum level of log messages sent to the client
42
42
  # to WARNING, where PostgreSQL uses a default of NOTICE. This is to avoid a lot
@@ -55,14 +55,14 @@ module Sequel
55
55
  # Methods shared by adapter/connection instances.
56
56
  module AdapterMethods
57
57
  attr_writer :db
58
-
58
+
59
59
  SELECT_CURRVAL = "SELECT currval('%s')".freeze
60
60
  SELECT_CUSTOM_SEQUENCE = proc do |schema, table| <<-end_sql
61
- SELECT '"' || name.nspname || '".' || CASE
62
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
63
- substr(split_part(def.adsrc, '''', 2),
64
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
65
- ELSE split_part(def.adsrc, '''', 2)
61
+ SELECT '"' || name.nspname || '".' || CASE
62
+ WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
63
+ substr(split_part(def.adsrc, '''', 2),
64
+ strpos(split_part(def.adsrc, '''', 2), '.')+1)
65
+ ELSE split_part(def.adsrc, '''', 2)
66
66
  END
67
67
  FROM pg_class t
68
68
  JOIN pg_namespace name ON (t.relnamespace = name.oid)
@@ -103,11 +103,11 @@ module Sequel
103
103
  AND seq.relname = '#{table}'
104
104
  end_sql
105
105
  end
106
-
106
+
107
107
  # Depth of the current transaction on this connection, used
108
108
  # to implement multi-level transactions with savepoints.
109
109
  attr_accessor :transaction_depth
110
-
110
+
111
111
  # Apply connection settings for this connection. Currently, turns
112
112
  # standard_conforming_strings ON if Postgres.force_standard_strings
113
113
  # is true.
@@ -131,7 +131,7 @@ module Sequel
131
131
  return val.to_i if val
132
132
  end
133
133
  end
134
-
134
+
135
135
  # Get the primary key for the given table.
136
136
  def primary_key(schema, table)
137
137
  sql = SELECT_PK[schema, table]
@@ -139,7 +139,7 @@ module Sequel
139
139
  return single_value(r)
140
140
  end
141
141
  end
142
-
142
+
143
143
  # Get the primary key and sequence for the given table.
144
144
  def sequence(schema, table)
145
145
  sql = SELECT_SERIAL_SEQUENCE[schema, table]
@@ -147,14 +147,14 @@ module Sequel
147
147
  seq = single_value(r)
148
148
  return seq if seq
149
149
  end
150
-
150
+
151
151
  sql = SELECT_CUSTOM_SEQUENCE[schema, table]
152
152
  execute(sql) do |r|
153
153
  return single_value(r)
154
154
  end
155
155
  end
156
156
  end
157
-
157
+
158
158
  # Methods shared by Database instances that connect to PostgreSQL.
159
159
  module DatabaseMethods
160
160
  EXCLUDE_SCHEMAS = /pg_*|information_schema/i
@@ -191,7 +191,7 @@ module Sequel
191
191
  def create_function(name, definition, opts={})
192
192
  self << create_function_sql(name, definition, opts)
193
193
  end
194
-
194
+
195
195
  # Create the procedural language in the database. Arguments:
196
196
  # * name : Name of the procedural language (e.g. plpgsql)
197
197
  # * opts : options hash:
@@ -202,7 +202,13 @@ module Sequel
202
202
  def create_language(name, opts={})
203
203
  self << create_language_sql(name, opts)
204
204
  end
205
-
205
+
206
+ # Create a schema in the database. Arguments:
207
+ # * name : Name of the schema (e.g. admin)
208
+ def create_schema(name)
209
+ self << create_schema_sql(name)
210
+ end
211
+
206
212
  # Create a trigger in the database. Arguments:
207
213
  # * table : the table on which this trigger operates
208
214
  # * name : the name of this trigger
@@ -216,7 +222,7 @@ module Sequel
216
222
  def create_trigger(table, name, function, opts={})
217
223
  self << create_trigger_sql(table, name, function, opts)
218
224
  end
219
-
225
+
220
226
  # PostgreSQL uses the :postgres database type.
221
227
  def database_type
222
228
  :postgres
@@ -231,7 +237,7 @@ module Sequel
231
237
  def drop_function(name, opts={})
232
238
  self << drop_function_sql(name, opts)
233
239
  end
234
-
240
+
235
241
  # Drops a procedural language from the database. Arguments:
236
242
  # * name : name of the procedural language to drop
237
243
  # * opts : options hash:
@@ -240,7 +246,16 @@ module Sequel
240
246
  def drop_language(name, opts={})
241
247
  self << drop_language_sql(name, opts)
242
248
  end
243
-
249
+
250
+ # Drops a schema from the database. Arguments:
251
+ # * name : name of the schema to drop
252
+ # * opts : options hash:
253
+ # * :cascade : Drop all objects in this schema.
254
+ # * :if_exists : Don't raise an error if the schema doesn't exist.
255
+ def drop_schema(name, opts={})
256
+ self << drop_schema_sql(name, opts)
257
+ end
258
+
244
259
  # Drops a trigger from the database. Arguments:
245
260
  # * table : table from which to drop the trigger
246
261
  # * name : name of the trigger to drop
@@ -250,7 +265,7 @@ module Sequel
250
265
  def drop_trigger(table, name, opts={})
251
266
  self << drop_trigger_sql(table, name, opts)
252
267
  end
253
-
268
+
254
269
  # Use the pg_* system tables to determine indexes on a table
255
270
  def indexes(table, opts={})
256
271
  m = output_identifier_meth
@@ -266,11 +281,11 @@ module Sequel
266
281
  filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil).
267
282
  order(:indc__relname, range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}.case(32, :att__attnum)).
268
283
  select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column)
269
-
284
+
270
285
  ds.join!(:pg_namespace___nsp, :oid=>:tab__relnamespace, :nspname=>schema.to_s) if schema
271
286
  ds.filter!(:indisvalid=>true) if server_version >= 80200
272
287
  ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
273
-
288
+
274
289
  indexes = {}
275
290
  ds.each do |r|
276
291
  i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique]}
@@ -279,11 +294,11 @@ module Sequel
279
294
  indexes
280
295
  end
281
296
 
282
- # Dataset containing all current database locks
297
+ # Dataset containing all current database locks
283
298
  def locks
284
299
  dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
285
300
  end
286
-
301
+
287
302
  # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
288
303
  #
289
304
  # :payload :: The payload string to use for the NOTIFY statement. Only supported
@@ -304,7 +319,7 @@ module Sequel
304
319
  synchronize(opts[:server]){|con| con.primary_key(*schema_and_table(table))}
305
320
  end
306
321
  end
307
-
322
+
308
323
  # Return the sequence providing the default for the primary key for the given table.
309
324
  def primary_key_sequence(table, opts={})
310
325
  quoted_table = quote_schema_table(table)
@@ -315,7 +330,7 @@ module Sequel
315
330
  synchronize(opts[:server]){|con| con.sequence(*schema_and_table(table))}
316
331
  end
317
332
  end
318
-
333
+
319
334
  # Reset the primary key sequence for the given table, baseing it on the
320
335
  # maximum current value of the table's primary key.
321
336
  def reset_primary_key_sequence(table)
@@ -337,7 +352,7 @@ module Sequel
337
352
  def serial_primary_key_options
338
353
  {:primary_key => true, :serial => true, :type=>Integer}
339
354
  end
340
-
355
+
341
356
  # The version of the PostgreSQL server, used for determining capability.
342
357
  def server_version(server=nil)
343
358
  return @server_version if @server_version
@@ -353,7 +368,7 @@ module Sequel
353
368
  end
354
369
  @server_version
355
370
  end
356
-
371
+
357
372
  # PostgreSQL supports prepared transactions (two-phase commit) if
358
373
  # max_prepared_transactions is greater than 0.
359
374
  def supports_prepared_transactions?
@@ -378,7 +393,7 @@ module Sequel
378
393
 
379
394
  # Array of symbols specifying table names in the current database.
380
395
  # The dataset used is yielded to the block if one is provided,
381
- # otherwise, an array of symbols of table names is returned.
396
+ # otherwise, an array of symbols of table names is returned.
382
397
  #
383
398
  # Options:
384
399
  # * :schema - The schema to search (default_schema by default)
@@ -428,34 +443,44 @@ module Sequel
428
443
  AS #{literal(definition.to_s)}#{", #{literal(opts[:link_symbol].to_s)}" if opts[:link_symbol]}
429
444
  END
430
445
  end
431
-
446
+
432
447
  # SQL for creating a procedural language.
433
448
  def create_language_sql(name, opts={})
434
449
  "CREATE#{' OR REPLACE' if opts[:replace] && server_version >= 90000}#{' TRUSTED' if opts[:trusted]} LANGUAGE #{name}#{" HANDLER #{opts[:handler]}" if opts[:handler]}#{" VALIDATOR #{opts[:validator]}" if opts[:validator]}"
435
450
  end
436
-
437
- # SQL for creating a database trigger.
451
+
452
+ # SQL for creating a schema.
453
+ def create_schema_sql(name)
454
+ "CREATE SCHEMA #{quote_identifier(name)}"
455
+ end
456
+
457
+ # SQL for creating a database trigger.
438
458
  def create_trigger_sql(table, name, function, opts={})
439
459
  events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
440
460
  whence = opts[:after] ? 'AFTER' : 'BEFORE'
441
461
  "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(', ')})"
442
462
  end
443
-
463
+
444
464
  # The errors that the main adapters can raise, depends on the adapter being used
445
465
  def database_error_classes
446
466
  CONVERTED_EXCEPTIONS
447
467
  end
448
-
449
- # SQL for dropping a function from the database.
468
+
469
+ # SQL for dropping a function from the database.
450
470
  def drop_function_sql(name, opts={})
451
471
  "DROP FUNCTION#{' IF EXISTS' if opts[:if_exists]} #{name}#{sql_function_args(opts[:args])}#{' CASCADE' if opts[:cascade]}"
452
472
  end
453
-
473
+
454
474
  # SQL for dropping a procedural language from the database.
455
475
  def drop_language_sql(name, opts={})
456
476
  "DROP LANGUAGE#{' IF EXISTS' if opts[:if_exists]} #{name}#{' CASCADE' if opts[:cascade]}"
457
477
  end
458
478
 
479
+ # SQL for dropping a schema from the database.
480
+ def drop_schema_sql(name, opts={})
481
+ "DROP SCHEMA#{' IF EXISTS' if opts[:if_exists]} #{quote_identifier(name)}#{' CASCADE' if opts[:cascade]}"
482
+ end
483
+
459
484
  # SQL for dropping a trigger from the database.
460
485
  def drop_trigger_sql(table, name, opts={})
461
486
  "DROP TRIGGER#{' IF EXISTS' if opts[:if_exists]} #{name} ON #{quote_schema_table(table)}#{' CASCADE' if opts[:cascade]}"
@@ -470,12 +495,12 @@ module Sequel
470
495
  ds.exclude(:pg_namespace__nspname=>EXCLUDE_SCHEMAS)
471
496
  end
472
497
  end
473
-
498
+
474
499
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on input.
475
500
  def identifier_input_method_default
476
501
  nil
477
502
  end
478
-
503
+
479
504
  # PostgreSQL folds unquoted identifiers to lowercase, so it shouldn't need to upcase identifiers on output.
480
505
  def identifier_output_method_default
481
506
  nil
@@ -485,7 +510,7 @@ module Sequel
485
510
  def index_definition_sql(table_name, index)
486
511
  cols = index[:columns]
487
512
  index_name = index[:name] || default_index_name(table_name, cols)
488
- expr = if o = index[:opclass]
513
+ expr = if o = index[:opclass]
489
514
  "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
490
515
  else
491
516
  literal(Array(cols))
@@ -503,7 +528,7 @@ module Sequel
503
528
  end
504
529
  "CREATE #{unique}INDEX #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{filter}"
505
530
  end
506
-
531
+
507
532
  # The result of the insert for the given table and values. If values
508
533
  # is an array, assume the first column is the primary key and return
509
534
  # that. If values is a hash, lookup the primary key for the table. If
@@ -533,13 +558,13 @@ module Sequel
533
558
  end
534
559
 
535
560
  # Don't log, since logging is done by the underlying connection.
536
- def log_connection_execute(conn, sql)
561
+ def log_connection_execute(conn, sql)
537
562
  conn.execute(sql)
538
563
  end
539
-
564
+
540
565
  # Backbone of the tables and views support.
541
566
  def pg_class_relname(type, opts)
542
- ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
567
+ ds = metadata_dataset.from(:pg_class).filter(:relkind=>type).select(:relname).exclude(SQL::StringExpression.like(:relname, SYSTEM_TABLE_REGEXP)).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
543
568
  ds = filter_schema(ds, opts)
544
569
  m = output_identifier_meth
545
570
  block_given? ? yield(ds) : ds.map{|r| m.call(r[:relname])}
@@ -550,7 +575,7 @@ module Sequel
550
575
  def prepared_arg_placeholder
551
576
  PREPARED_ARG_PLACEHOLDER
552
577
  end
553
-
578
+
554
579
  # Remove the cached entries for primary keys and sequences when a table is
555
580
  # changed.
556
581
  def remove_cached_schema(table)
@@ -564,7 +589,7 @@ module Sequel
564
589
  # a rename table operation, so speciying a new schema in new_name will not have an effect.
565
590
  def rename_table_sql(name, new_name)
566
591
  "ALTER TABLE #{quote_schema_table(name)} RENAME TO #{quote_identifier(schema_and_table(new_name).last)}"
567
- end
592
+ end
568
593
 
569
594
  # PostgreSQL's autoincrementing primary keys are of type integer or bigint
570
595
  # using a nextval function call as a default.
@@ -613,7 +638,7 @@ module Sequel
613
638
  def sql_function_args(args)
614
639
  "(#{Array(args).map{|a| Array(a).reverse.join(' ')}.join(', ')})"
615
640
  end
616
-
641
+
617
642
  # Handle bigserial type if :serial option is present
618
643
  def type_literal_generic_bignum(column)
619
644
  column[:serial] ? :bigserial : super
@@ -643,7 +668,7 @@ module Sequel
643
668
  end
644
669
  end
645
670
  end
646
-
671
+
647
672
  # Instance methods for datasets that connect to a PostgreSQL database.
648
673
  module DatasetMethods
649
674
  ACCESS_SHARE = 'ACCESS SHARE'.freeze
@@ -688,7 +713,7 @@ module Sequel
688
713
  BLOB_RE = /[\000-\037\047\134\177-\377]/n.freeze
689
714
  WINDOW = " WINDOW ".freeze
690
715
  EMPTY_STRING = ''.freeze
691
-
716
+
692
717
  # Shared methods for prepared statements when used with PostgreSQL databases.
693
718
  module PreparedStatementMethods
694
719
  # Override insert action to use RETURNING if the server supports it.
@@ -722,7 +747,7 @@ module Sequel
722
747
  def analyze
723
748
  explain(:analyze=>true)
724
749
  end
725
-
750
+
726
751
  # Handle converting the ruby xor operator (^) into the
727
752
  # PostgreSQL xor operator (#).
728
753
  def complex_expression_sql_append(sql, op, args)
@@ -749,12 +774,12 @@ module Sequel
749
774
  def explain(opts={})
750
775
  with_sql((opts[:analyze] ? EXPLAIN_ANALYZE : EXPLAIN) + select_sql).map(QUERY_PLAN).join(CRLF)
751
776
  end
752
-
777
+
753
778
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
754
779
  def for_share
755
780
  lock_style(:share)
756
781
  end
757
-
782
+
758
783
  # PostgreSQL specific full text search syntax, using tsearch2 (included
759
784
  # in 8.3 by default, and available for earlier versions as an add-on).
760
785
  def full_text_search(cols, terms, opts = {})
@@ -762,7 +787,7 @@ module Sequel
762
787
  terms = terms.join(' | ') if terms.is_a?(Array)
763
788
  filter("to_tsvector(?, ?) @@ to_tsquery(?, ?)", lang, full_text_string_join(cols), lang, terms)
764
789
  end
765
-
790
+
766
791
  # Insert given values into the database.
767
792
  def insert(*values, &block)
768
793
  if @opts[:returning]
@@ -802,17 +827,17 @@ module Sequel
802
827
  end
803
828
  nil
804
829
  end
805
-
830
+
806
831
  # For PostgreSQL version > 8.2, allow inserting multiple rows at once.
807
832
  def multi_insert_sql(columns, values)
808
833
  return super if server_version < 80200
809
-
834
+
810
835
  # postgresql 8.2 introduces support for multi-row insert
811
836
  sql = LiteralString.new('VALUES ')
812
837
  expression_list_append(sql, values.map{|r| Array(r)})
813
838
  [insert_sql(columns, sql)]
814
839
  end
815
-
840
+
816
841
  # PostgreSQL supports using the WITH clause in subqueries if it
817
842
  # supports using WITH at all (i.e. on PostgreSQL 8.4+).
818
843
  def supports_cte_in_subqueries?
@@ -823,7 +848,7 @@ module Sequel
823
848
  def supports_distinct_on?
824
849
  true
825
850
  end
826
-
851
+
827
852
  # PostgreSQL supports modifying joined datasets
828
853
  def supports_modifying_joins?
829
854
  true
@@ -841,7 +866,7 @@ module Sequel
841
866
  def supports_timestamp_timezones?
842
867
  true
843
868
  end
844
-
869
+
845
870
  # PostgreSQL 8.4+ supports window functions
846
871
  def supports_window_functions?
847
872
  server_version >= 80400
@@ -851,7 +876,7 @@ module Sequel
851
876
  def window(name, opts)
852
877
  clone(:window=>(@opts[:window]||[]) + [[name, SQL::Window.new(opts)]])
853
878
  end
854
-
879
+
855
880
  protected
856
881
 
857
882
  # If returned primary keys are requested, use RETURNING unless already set on the
@@ -876,11 +901,11 @@ module Sequel
876
901
  end
877
902
 
878
903
  private
879
-
904
+
880
905
  # PostgreSQL allows deleting from joined datasets
881
906
  def delete_clause_methods
882
907
  server_version >= 90100 ? DELETE_CLAUSE_METHODS_91 : DELETE_CLAUSE_METHODS
883
- end
908
+ end
884
909
 
885
910
  # Only include the primary table in the main delete clause
886
911
  def delete_from_sql(sql)
@@ -931,6 +956,19 @@ module Sequel
931
956
  def literal_false
932
957
  BOOL_FALSE
933
958
  end
959
+
960
+ # PostgreSQL quotes NaN and Infinity.
961
+ def literal_float(value)
962
+ if value.finite?
963
+ super
964
+ elsif value.nan?
965
+ "'NaN'"
966
+ elsif value.infinite? == 1
967
+ "'Infinity'"
968
+ else
969
+ "'-Infinity'"
970
+ end
971
+ end
934
972
 
935
973
  # Assume that SQL standard quoting is on, per Sequel's defaults
936
974
  def literal_string_append(sql, v)
@@ -946,7 +984,7 @@ module Sequel
946
984
  def select_clause_methods
947
985
  server_version >= 80400 ? SELECT_CLAUSE_METHODS_84 : SELECT_CLAUSE_METHODS
948
986
  end
949
-
987
+
950
988
  # PostgreSQL requires parentheses around compound datasets if they use
951
989
  # CTEs, and using them in other places doesn't hurt.
952
990
  def compound_dataset_sql_append(sql, ds)
@@ -967,7 +1005,7 @@ module Sequel
967
1005
  c = false
968
1006
  co = COMMA
969
1007
  as = AS
970
- ws.map do |name, window|
1008
+ ws.map do |name, window|
971
1009
  sql << co if c
972
1010
  literal_append(sql, name)
973
1011
  sql << as
@@ -976,7 +1014,7 @@ module Sequel
976
1014
  end
977
1015
  end
978
1016
  end
979
-
1017
+
980
1018
  # Use WITH RECURSIVE instead of WITH if any of the CTEs is recursive
981
1019
  def select_with_sql_base
982
1020
  opts[:with].any?{|w| w[:recursive]} ? SQL_WITH_RECURSIVE : super