sequel 3.7.0 → 3.8.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 (42) hide show
  1. data/CHANGELOG +50 -0
  2. data/doc/advanced_associations.rdoc +4 -3
  3. data/doc/release_notes/3.7.0.txt +2 -2
  4. data/doc/release_notes/3.8.0.txt +151 -0
  5. data/lib/sequel/adapters/jdbc.rb +10 -2
  6. data/lib/sequel/adapters/jdbc/h2.rb +14 -0
  7. data/lib/sequel/adapters/mysql.rb +36 -26
  8. data/lib/sequel/adapters/postgres.rb +27 -19
  9. data/lib/sequel/adapters/shared/mssql.rb +12 -4
  10. data/lib/sequel/adapters/shared/mysql.rb +16 -0
  11. data/lib/sequel/connection_pool.rb +178 -57
  12. data/lib/sequel/database.rb +60 -12
  13. data/lib/sequel/database/schema_generator.rb +1 -2
  14. data/lib/sequel/dataset.rb +10 -1
  15. data/lib/sequel/dataset/actions.rb +4 -8
  16. data/lib/sequel/dataset/convenience.rb +9 -2
  17. data/lib/sequel/dataset/query.rb +2 -5
  18. data/lib/sequel/dataset/sql.rb +0 -1
  19. data/lib/sequel/exceptions.rb +3 -3
  20. data/lib/sequel/metaprogramming.rb +4 -18
  21. data/lib/sequel/model/associations.rb +2 -2
  22. data/lib/sequel/model/base.rb +15 -18
  23. data/lib/sequel/model/default_inflections.rb +0 -1
  24. data/lib/sequel/model/plugins.rb +3 -2
  25. data/lib/sequel/plugins/boolean_readers.rb +3 -2
  26. data/lib/sequel/plugins/identity_map.rb +9 -0
  27. data/lib/sequel/plugins/validation_helpers.rb +8 -1
  28. data/lib/sequel/sql.rb +21 -11
  29. data/lib/sequel/version.rb +1 -1
  30. data/spec/adapters/mssql_spec.rb +7 -1
  31. data/spec/adapters/mysql_spec.rb +22 -0
  32. data/spec/core/connection_pool_spec.rb +211 -3
  33. data/spec/core/core_sql_spec.rb +7 -0
  34. data/spec/core/database_spec.rb +159 -7
  35. data/spec/core/dataset_spec.rb +33 -0
  36. data/spec/core/spec_helper.rb +1 -0
  37. data/spec/extensions/boolean_readers_spec.rb +6 -0
  38. data/spec/extensions/identity_map_spec.rb +29 -1
  39. data/spec/extensions/inflector_spec.rb +0 -1
  40. data/spec/extensions/validation_helpers_spec.rb +23 -0
  41. data/spec/integration/type_test.rb +1 -1
  42. metadata +131 -129
@@ -8,7 +8,7 @@ module Sequel
8
8
  SQL_BEGIN = "BEGIN TRANSACTION".freeze
9
9
  SQL_COMMIT = "COMMIT TRANSACTION".freeze
10
10
  SQL_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION".freeze
11
- SQL_ROLLBACK_TO_SAVEPOINT = 'ROLLBACK TRANSACTION autopoint_%d'.freeze
11
+ SQL_ROLLBACK_TO_SAVEPOINT = 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION autopoint_%d'.freeze
12
12
  SQL_SAVEPOINT = 'SAVE TRANSACTION autopoint_%d'.freeze
13
13
  TEMPORARY = "#".freeze
14
14
 
@@ -183,7 +183,7 @@ module Sequel
183
183
  COMMA_SEPARATOR = ', '.freeze
184
184
  DELETE_CLAUSE_METHODS = Dataset.clause_methods(:delete, %w'with from output from2 where')
185
185
  INSERT_CLAUSE_METHODS = Dataset.clause_methods(:insert, %w'with into columns output values')
186
- SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with limit distinct columns from table_options join where group order having compounds')
186
+ SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'with limit distinct columns from table_options join where group having order compounds')
187
187
  UPDATE_CLAUSE_METHODS = Dataset.clause_methods(:update, %w'with table set output from where')
188
188
  WILDCARD = LiteralString.new('*').freeze
189
189
  CONSTANT_MAP = {:CURRENT_DATE=>'CAST(CURRENT_TIMESTAMP AS DATE)'.freeze, :CURRENT_TIME=>'CAST(CURRENT_TIMESTAMP AS TIME)'.freeze}
@@ -230,6 +230,7 @@ module Sequel
230
230
 
231
231
  # Use the OUTPUT clause to get the value of all columns for the newly inserted record.
232
232
  def insert_select(*values)
233
+ return unless supports_output_clause?
233
234
  naked.clone(default_server_opts(:sql=>output(nil, [:inserted.*]).insert_sql(*values))).single_record unless opts[:disable_insert_output]
234
235
  end
235
236
 
@@ -256,6 +257,7 @@ module Sequel
256
257
  # dataset.output(:output_table, [:deleted__id, :deleted__name])
257
258
  # dataset.output(:output_table, :id => :inserted__id, :name => :inserted__name)
258
259
  def output(into, values)
260
+ raise(Error, "SQL Server versions 2000 and earlier do not support the OUTPUT clause") unless supports_output_clause?
259
261
  output = {}
260
262
  case values
261
263
  when Hash
@@ -332,6 +334,11 @@ module Sequel
332
334
  def supports_multiple_column_in?
333
335
  false
334
336
  end
337
+
338
+ # Only 2005+ supports the output clause.
339
+ def supports_output_clause?
340
+ server_version >= 9000000
341
+ end
335
342
 
336
343
  # MSSQL 2005+ supports window functions
337
344
  def supports_window_functions?
@@ -388,9 +395,9 @@ module Sequel
388
395
  blob
389
396
  end
390
397
 
391
- # Use unicode string syntax for all strings
398
+ # Use unicode string syntax for all strings. Don't double backslashes.
392
399
  def literal_string(v)
393
- "N#{super}"
400
+ "N'#{v.gsub(/'/, "''")}'"
394
401
  end
395
402
 
396
403
  # Use 0 for false on MSSQL
@@ -425,6 +432,7 @@ module Sequel
425
432
 
426
433
  # SQL fragment for MSSQL's OUTPUT clause.
427
434
  def output_sql(sql)
435
+ return unless supports_output_clause?
428
436
  return unless output = @opts[:output]
429
437
  sql << " OUTPUT #{column_list(output[:select_list])}"
430
438
  if into = output[:into]
@@ -179,6 +179,22 @@ module Sequel
179
179
  end
180
180
  end
181
181
 
182
+ # Respect the :size option if given to produce
183
+ # tinyblob, mediumblob, and longblob if :tiny,
184
+ # :medium, or :long is given.
185
+ def type_literal_generic_file(column)
186
+ case column[:size]
187
+ when :tiny # < 2^8 bytes
188
+ :tinyblob
189
+ when :medium # < 2^24 bytes
190
+ :mediumblob
191
+ when :long # < 2^32 bytes
192
+ :longblob
193
+ else # 2^16 bytes
194
+ :blob
195
+ end
196
+ end
197
+
182
198
  # MySQL has both datetime and timestamp classes, most people are going
183
199
  # to want datetime
184
200
  def type_literal_generic_datetime(column)
@@ -45,39 +45,75 @@ class Sequel::ConnectionPool
45
45
  raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
46
46
  @mutex = Mutex.new
47
47
  @connection_proc = block
48
- @disconnection_proc = opts[:disconnection_proc]
49
- @servers = [:default]
50
- @servers += opts[:servers].keys - @servers if opts[:servers]
51
- @available_connections = Hash.new{|h,k| h[:default]}
52
- @allocated = Hash.new{|h,k| h[:default]}
53
- @servers.each do |s|
54
- @available_connections[s] = []
55
- @allocated[s] = {}
56
- end
48
+ @disconnection_proc = opts[:disconnection_proc]
49
+ @available_connections = {}
50
+ @allocated = {}
51
+ @connections_to_remove = []
52
+ @servers = Hash.new(:default)
53
+ add_servers([:default])
54
+ add_servers(opts[:servers].keys) if opts[:servers]
57
55
  @timeout = Integer(opts[:pool_timeout] || 5)
58
56
  @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
59
57
  @convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
60
58
  end
61
59
 
60
+ # Adds new servers to the connection pool. Primarily used in conjunction with master/slave
61
+ # or shard configurations. Allows for dynamic expansion of the potential slaves/shards
62
+ # at runtime. servers argument should be an array of symbols.
63
+ def add_servers(servers)
64
+ sync do
65
+ servers.each do |server|
66
+ unless @servers.has_key?(server)
67
+ @servers[server] = server
68
+ @available_connections[server] = []
69
+ @allocated[server] = {}
70
+ end
71
+ end
72
+ end
73
+ end
74
+
62
75
  # A hash of connections currently being used for the given server, key is the
63
- # Thread, value is the connection.
76
+ # Thread, value is the connection. Nonexistent servers will return nil. Treat
77
+ # this as read only, do not modify the resulting object.
64
78
  def allocated(server=:default)
65
79
  @allocated[server]
66
80
  end
67
81
 
68
82
  # An array of connections opened but not currently used, for the given
69
- # server.
83
+ # server. Nonexistent servers will return nil. Treat this as read only, do
84
+ # not modify the resulting object.
70
85
  def available_connections(server=:default)
71
86
  @available_connections[server]
72
87
  end
73
88
 
74
89
  # The total number of connections opened for the given server, should
75
- # be equal to available_connections.length + allocated.length
90
+ # be equal to available_connections.length + allocated.length. Nonexistent
91
+ # servers will return the created count of the default server.
76
92
  def created_count(server=:default)
93
+ server = @servers[server]
77
94
  @allocated[server].length + @available_connections[server].length
78
95
  end
79
96
  alias size created_count
80
97
 
98
+ # Removes all connection currently available on all servers, optionally
99
+ # yielding each connection to the given block. This method has the effect of
100
+ # disconnecting from the database, assuming that no connections are currently
101
+ # being used. If connections are being used, they are scheduled to be
102
+ # disconnected as soon as they are returned to the pool.
103
+ #
104
+ # Once a connection is requested using #hold, the connection pool
105
+ # creates new connections to the database. Options:
106
+ # * :server - Should be a symbol specifing the server to disconnect from,
107
+ # or an array of symbols to specify multiple servers.
108
+ def disconnect(opts={}, &block)
109
+ block ||= @disconnection_proc
110
+ sync do
111
+ (opts[:server] ? Array(opts[:server]) : @servers.keys).each do |s|
112
+ disconnect_server(s, &block)
113
+ end
114
+ end
115
+ end
116
+
81
117
  # Chooses the first available connection to the given server, or if none are
82
118
  # available, creates a new connection. Passes the connection to the supplied
83
119
  # block:
@@ -94,27 +130,28 @@ class Sequel::ConnectionPool
94
130
  # raised.
95
131
  def hold(server=:default)
96
132
  begin
133
+ sync{server = @servers[server]}
97
134
  t = Thread.current
98
135
  if conn = owned_connection(t, server)
99
136
  return yield(conn)
100
137
  end
101
138
  begin
102
139
  unless conn = acquire(t, server)
103
- time = Time.new
140
+ time = Time.now
104
141
  timeout = time + @timeout
105
142
  sleep_time = @sleep_time
106
143
  sleep sleep_time
107
144
  until conn = acquire(t, server)
108
- raise(::Sequel::PoolTimeout) if Time.new > timeout
145
+ raise(::Sequel::PoolTimeout) if Time.now > timeout
109
146
  sleep sleep_time
110
147
  end
111
148
  end
112
149
  yield conn
113
- rescue Sequel::DatabaseDisconnectError => dde
114
- remove(t, conn, server) if conn
150
+ rescue Sequel::DatabaseDisconnectError
151
+ sync{@connections_to_remove << conn} if conn
115
152
  raise
116
153
  ensure
117
- @mutex.synchronize{release(t, server)} if conn && !dde
154
+ sync{release(t, conn, server)} if conn
118
155
  end
119
156
  rescue StandardError
120
157
  raise
@@ -122,28 +159,37 @@ class Sequel::ConnectionPool
122
159
  raise(@convert_exceptions ? RuntimeError.new(e.message) : e)
123
160
  end
124
161
  end
125
-
126
- # Removes all connection currently available on all servers, optionally
127
- # yielding each connection to the given block. This method has the effect of
128
- # disconnecting from the database, assuming that no connections are currently
129
- # being used. Once a connection is requested using #hold, the connection pool
130
- # creates new connections to the database.
131
- def disconnect(&block)
132
- block ||= @disconnection_proc
133
- @mutex.synchronize do
134
- @available_connections.each do |server, conns|
135
- conns.each{|c| block.call(c)} if block
136
- conns.clear
162
+
163
+ # Remove servers from the connection pool. Primarily used in conjunction with master/slave
164
+ # or shard configurations. Similar to disconnecting from all given servers,
165
+ # except that after it is used, future requests for the server will use the
166
+ # :default server instead.
167
+ def remove_servers(servers)
168
+ sync do
169
+ raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
170
+ servers.each do |server|
171
+ if @servers.include?(server)
172
+ disconnect_server(server, &@disconnection_proc)
173
+ @available_connections.delete(server)
174
+ @allocated.delete(server)
175
+ @servers.delete(server)
176
+ end
137
177
  end
138
178
  end
139
179
  end
140
-
180
+
181
+ # Return an array of symbols for servers in the connection pool.
182
+ def servers
183
+ sync{@servers.keys}
184
+ end
185
+
141
186
  private
142
187
 
143
188
  # Assigns a connection to the supplied thread for the given server, if one
144
- # is available.
189
+ # is available. The calling code should NOT already have the mutex when
190
+ # calling this.
145
191
  def acquire(thread, server)
146
- @mutex.synchronize do
192
+ sync do
147
193
  if conn = available(server)
148
194
  allocated(server)[thread] = conn
149
195
  end
@@ -151,16 +197,30 @@ class Sequel::ConnectionPool
151
197
  end
152
198
 
153
199
  # Returns an available connection to the given server. If no connection is
154
- # available, tries to create a new connection.
200
+ # available, tries to create a new connection. The calling code should already
201
+ # have the mutex before calling this.
155
202
  def available(server)
156
203
  available_connections(server).pop || make_new(server)
157
204
  end
158
-
205
+
206
+ # Disconnect from the given server. Disconnects available connections
207
+ # immediately, and schedules currently allocated connections for disconnection
208
+ # as soon as they are returned to the pool. The calling code should already
209
+ # have the mutex before calling this.
210
+ def disconnect_server(server, &block)
211
+ if conns = available_connections(server)
212
+ conns.each{|conn| block.call(conn)} if block
213
+ conns.clear
214
+ end
215
+ @connections_to_remove.concat(allocated(server).values)
216
+ end
217
+
159
218
  # Creates a new connection to the given server if the size of the pool for
160
- # the server is less than the maximum size of the pool.
219
+ # the server is less than the maximum size of the pool. The calling code
220
+ # should already have the mutex before calling this.
161
221
  def make_new(server)
162
222
  if (n = created_count(server)) >= @max_size
163
- allocated(server).keys.reject{|t| t.alive?}.each{|t| release(t, server)}
223
+ allocated(server).to_a.each{|t, c| release(t, c, server) unless t.alive?}
164
224
  n = nil
165
225
  end
166
226
  if (n || created_count(server)) < @max_size
@@ -176,23 +236,35 @@ class Sequel::ConnectionPool
176
236
  end
177
237
 
178
238
  # Returns the connection owned by the supplied thread for the given server,
179
- # if any.
239
+ # if any. The calling code should NOT already have the mutex before calling this.
180
240
  def owned_connection(thread, server)
181
- @mutex.synchronize{@allocated[server][thread]}
241
+ sync{@allocated[server][thread]}
182
242
  end
183
243
 
184
- # Releases the connection assigned to the supplied thread and server.
185
- # You must already have the mutex before you call this.
186
- def release(thread, server)
187
- available_connections(server) << allocated(server).delete(thread)
244
+ # Releases the connection assigned to the supplied thread and server. If the
245
+ # server or connection given is scheduled for disconnection, remove the
246
+ # connection instead of releasing it back to the pool.
247
+ # The calling code should already have the mutex before calling this.
248
+ def release(thread, conn, server)
249
+ if @connections_to_remove.include?(conn)
250
+ remove(thread, conn, server)
251
+ else
252
+ available_connections(server) << allocated(server).delete(thread)
253
+ end
188
254
  end
189
255
 
190
- # Removes the currently allocated connection from the connection pool.
256
+ # Removes the currently allocated connection from the connection pool. The
257
+ # calling code should already have the mutex before calling this.
191
258
  def remove(thread, conn, server)
192
- @mutex.synchronize do
193
- allocated(server).delete(thread)
194
- @disconnection_proc.call(conn) if @disconnection_proc
195
- end
259
+ @connections_to_remove.delete(conn)
260
+ allocated(server).delete(thread) if @servers.include?(server)
261
+ @disconnection_proc.call(conn) if @disconnection_proc
262
+ end
263
+
264
+ # Yield to the block while inside the mutex. The calling code should NOT
265
+ # already have the mutex before calling this.
266
+ def sync
267
+ @mutex.synchronize{yield}
196
268
  end
197
269
  end
198
270
 
@@ -220,12 +292,31 @@ class Sequel::SingleThreadedPool
220
292
  @connection_proc = block
221
293
  @disconnection_proc = opts[:disconnection_proc]
222
294
  @conns = {}
295
+ @servers = Hash.new(:default)
296
+ add_servers([:default])
297
+ add_servers(opts[:servers].keys) if opts[:servers]
223
298
  @convert_exceptions = opts.include?(:pool_convert_exceptions) ? opts[:pool_convert_exceptions] : true
224
299
  end
225
300
 
301
+ # Adds new servers to the connection pool. Primarily used in conjunction with master/slave
302
+ # or shard configurations. Allows for dynamic expansion of the potential slaves/shards
303
+ # at runtime. servers argument should be an array of symbols.
304
+ def add_servers(servers)
305
+ servers.each{|s| @servers[s] = s}
306
+ end
307
+
226
308
  # The connection for the given server.
227
309
  def conn(server=:default)
228
- @conns[server]
310
+ @conns[@servers[server]]
311
+ end
312
+
313
+ # Disconnects from the database. Once a connection is requested using
314
+ # #hold, the connection is reestablished. Options:
315
+ # * :server - Should be a symbol specifing the server to disconnect from,
316
+ # or an array of symbols to specify multiple servers.
317
+ def disconnect(opts={}, &block)
318
+ block ||= @disconnection_proc
319
+ (opts[:server] ? Array(opts[:server]) : servers).each{|s| disconnect_server(s, &block)}
229
320
  end
230
321
 
231
322
  # Yields the connection to the supplied block for the given server.
@@ -233,10 +324,10 @@ class Sequel::SingleThreadedPool
233
324
  def hold(server=:default)
234
325
  begin
235
326
  begin
236
- yield(c = (@conns[server] ||= @connection_proc.call(server)))
327
+ server = @servers[server]
328
+ yield(c = (@conns[server] ||= make_new(server)))
237
329
  rescue Sequel::DatabaseDisconnectError
238
- @conns.delete(server)
239
- @disconnection_proc.call(c) if @disconnection_proc
330
+ disconnect_server(server, &@disconnection_proc)
240
331
  raise
241
332
  end
242
333
  rescue Exception => e
@@ -245,11 +336,41 @@ class Sequel::SingleThreadedPool
245
336
  end
246
337
  end
247
338
 
248
- # Disconnects from the database. Once a connection is requested using
249
- # #hold, the connection is reestablished.
250
- def disconnect(&block)
251
- block ||= @disconnection_proc
252
- @conns.values.each{|conn| block.call(conn) if block}
253
- @conns = {}
339
+ # Remove servers from the connection pool. Primarily used in conjunction with master/slave
340
+ # or shard configurations. Similar to disconnecting from all given servers,
341
+ # except that after it is used, future requests for the server will use the
342
+ # :default server instead.
343
+ def remove_servers(servers)
344
+ raise(Sequel::Error, "cannot remove default server") if servers.include?(:default)
345
+ servers.each do |server|
346
+ disconnect_server(server, &@disconnection_proc)
347
+ @servers.delete(server)
348
+ end
349
+ end
350
+
351
+ # Return an array of symbols for servers in the connection pool.
352
+ def servers
353
+ @servers.keys
354
+ end
355
+
356
+ private
357
+
358
+ # Disconnect from the given server, if connected.
359
+ def disconnect_server(server, &block)
360
+ if conn = @conns.delete(server)
361
+ block.call(conn) if block
362
+ end
363
+ end
364
+
365
+ # Return a connection to the given server, raising DatabaseConnectionError
366
+ # if the connection_proc raises an error or doesn't return a valid connection.
367
+ def make_new(server)
368
+ begin
369
+ conn = @connection_proc.call(server)
370
+ rescue Exception=>exception
371
+ raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
372
+ end
373
+ raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
374
+ conn
254
375
  end
255
376
  end
@@ -255,6 +255,20 @@ module Sequel
255
255
  (String === args.first) ? fetch(*args) : from(*args)
256
256
  end
257
257
 
258
+ # Dynamically add new servers or modify server options at runtime. Also adds new
259
+ # servers to the connection pool. Intended for use with master/slave or shard
260
+ # configurations where it is useful to add new server hosts at runtime.
261
+ #
262
+ # servers argument should be a hash with server name symbol keys and hash or
263
+ # proc values. If a servers key is already in use, it's value is overridden
264
+ # with the value provided.
265
+ #
266
+ # DB.add_servers(:f=>{:host=>"hash_host_f"})
267
+ def add_servers(servers)
268
+ @opts[:servers] = @opts[:servers] ? @opts[:servers].merge(servers) : servers
269
+ @pool.add_servers(servers.keys)
270
+ end
271
+
258
272
  # Call the prepared statement with the given name with the given hash
259
273
  # of arguments.
260
274
  def call(ps_name, hash={})
@@ -288,9 +302,20 @@ module Sequel
288
302
  end
289
303
 
290
304
  # Disconnects all available connections from the connection pool. Any
291
- # connections currently in use will not be disconnected.
292
- def disconnect
293
- pool.disconnect
305
+ # connections currently in use will not be disconnected. Options:
306
+ # * :servers - Should be a symbol specifing the server to disconnect from,
307
+ # or an array of symbols to specify multiple servers.
308
+ def disconnect(opts = {})
309
+ pool.disconnect(opts)
310
+ end
311
+
312
+ # Yield a new database object for every server in the connection pool.
313
+ # Intended for use in sharded environments where there is a need to make schema
314
+ # modifications (DDL queries) on each shard.
315
+ #
316
+ # DB.each_server{|db| db.create_table(:users){primary_key :id; String :name}}
317
+ def each_server(&block)
318
+ servers.each{|s| self.class.connect(server_opts(s), &block)}
294
319
  end
295
320
 
296
321
  # Executes the given SQL on the database. This method should be overridden in descendants.
@@ -431,6 +456,26 @@ module Sequel
431
456
  @quote_identifiers = @opts.include?(:quote_identifiers) ? @opts[:quote_identifiers] : (@@quote_identifiers.nil? ? quote_identifiers_default : @@quote_identifiers)
432
457
  end
433
458
 
459
+ # Dynamically remove existing servers from the connection pool. Intended for
460
+ # use with master/slave or shard configurations where it is useful to remove
461
+ # existing server hosts at runtime.
462
+ #
463
+ # servers should be symbols or arrays of symbols. If a nonexistent server
464
+ # is specified, it is ignored. If no servers have been specified for
465
+ # this database, no changes are made. If you attempt to remove the :default server,
466
+ # an error will be raised.
467
+ #
468
+ # DB.remove_servers(:f1, :f2)
469
+ def remove_servers(*servers)
470
+ if @opts[:servers] && !@opts[:servers].empty?
471
+ servs = @opts[:servers].dup
472
+ servers.flatten!
473
+ servers.each{|s| servs.delete(s)}
474
+ @opts[:servers] = servs
475
+ @pool.remove_servers(servers)
476
+ end
477
+ end
478
+
434
479
  # Runs the supplied SQL statement string on the database server. Returns nil.
435
480
  # Options:
436
481
  # * :server - The server to run the SQL on.
@@ -439,11 +484,6 @@ module Sequel
439
484
  nil
440
485
  end
441
486
 
442
- # Returns a new dataset with the select method invoked.
443
- def select(*args, &block)
444
- dataset.select(*args, &block)
445
- end
446
-
447
487
  # Parse the schema from the database.
448
488
  # Returns the schema for the given table as an array with all members being arrays of length 2,
449
489
  # the first member being the column name, and the second member being a hash of column information.
@@ -472,6 +512,16 @@ module Sequel
472
512
  @schemas[quoted_name] = cols
473
513
  end
474
514
 
515
+ # Returns a new dataset with the select method invoked.
516
+ def select(*args, &block)
517
+ dataset.select(*args, &block)
518
+ end
519
+
520
+ # An array of servers/shards for this Database object.
521
+ def servers
522
+ pool.servers
523
+ end
524
+
475
525
  # Returns true if the database is using a single-threaded connection pool.
476
526
  def single_threaded?
477
527
  @single_threaded
@@ -488,8 +538,7 @@ module Sequel
488
538
  end
489
539
 
490
540
  # Returns true if a table with the given name exists. This requires a query
491
- # to the database unless this database object already has the schema for
492
- # the given table name.
541
+ # to the database.
493
542
  def table_exists?(name)
494
543
  begin
495
544
  from(name).first
@@ -905,7 +954,7 @@ module Sequel
905
954
  opts.delete(:servers)
906
955
  opts
907
956
  end
908
-
957
+
909
958
  # Raise a database error unless the exception is an Rollback.
910
959
  def transaction_error(e)
911
960
  raise_error(e, :classes=>database_error_classes) unless e.is_a?(Rollback)
@@ -996,4 +1045,3 @@ module Sequel
996
1045
  end
997
1046
  end
998
1047
  end
999
-