sequel 3.7.0 → 3.8.0

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