sequel 3.45.0 → 3.46.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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +6 -0
  4. data/Rakefile +46 -33
  5. data/doc/release_notes/3.46.0.txt +122 -0
  6. data/doc/schema_modification.rdoc +42 -6
  7. data/doc/security.rdoc +379 -0
  8. data/doc/transactions.rdoc +1 -1
  9. data/lib/sequel/adapters/jdbc/as400.rb +1 -0
  10. data/lib/sequel/adapters/jdbc/h2.rb +11 -0
  11. data/lib/sequel/adapters/mysql2.rb +3 -9
  12. data/lib/sequel/adapters/postgres.rb +34 -2
  13. data/lib/sequel/adapters/shared/cubrid.rb +5 -0
  14. data/lib/sequel/adapters/shared/mssql.rb +27 -3
  15. data/lib/sequel/adapters/shared/mysql.rb +25 -4
  16. data/lib/sequel/adapters/shared/sqlite.rb +12 -1
  17. data/lib/sequel/connection_pool.rb +3 -3
  18. data/lib/sequel/connection_pool/sharded_threaded.rb +7 -8
  19. data/lib/sequel/connection_pool/threaded.rb +7 -8
  20. data/lib/sequel/core.rb +5 -2
  21. data/lib/sequel/database.rb +1 -1
  22. data/lib/sequel/database/connecting.rb +7 -7
  23. data/lib/sequel/database/features.rb +88 -0
  24. data/lib/sequel/database/misc.rb +14 -64
  25. data/lib/sequel/database/query.rb +0 -332
  26. data/lib/sequel/database/schema_generator.rb +36 -3
  27. data/lib/sequel/database/schema_methods.rb +48 -12
  28. data/lib/sequel/database/transactions.rb +344 -0
  29. data/lib/sequel/dataset/actions.rb +24 -9
  30. data/lib/sequel/dataset/mutation.rb +20 -0
  31. data/lib/sequel/dataset/query.rb +0 -17
  32. data/lib/sequel/dataset/sql.rb +7 -0
  33. data/lib/sequel/exceptions.rb +10 -6
  34. data/lib/sequel/extensions/_pretty_table.rb +2 -2
  35. data/lib/sequel/extensions/looser_typecasting.rb +10 -0
  36. data/lib/sequel/extensions/migration.rb +5 -2
  37. data/lib/sequel/model.rb +1 -1
  38. data/lib/sequel/model/associations.rb +16 -14
  39. data/lib/sequel/model/base.rb +14 -2
  40. data/lib/sequel/plugins/composition.rb +3 -3
  41. data/lib/sequel/plugins/dirty.rb +6 -6
  42. data/lib/sequel/plugins/hook_class_methods.rb +3 -0
  43. data/lib/sequel/plugins/serialization.rb +7 -17
  44. data/lib/sequel/plugins/string_stripper.rb +2 -1
  45. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  46. data/lib/sequel/sql.rb +3 -0
  47. data/lib/sequel/version.rb +1 -1
  48. data/spec/adapters/mssql_spec.rb +21 -0
  49. data/spec/adapters/postgres_spec.rb +35 -8
  50. data/spec/core/database_spec.rb +4 -0
  51. data/spec/core/dataset_spec.rb +48 -2
  52. data/spec/core/schema_generator_spec.rb +10 -1
  53. data/spec/core/schema_spec.rb +69 -0
  54. data/spec/extensions/composition_spec.rb +21 -2
  55. data/spec/extensions/dirty_spec.rb +17 -10
  56. data/spec/extensions/eager_each_spec.rb +4 -1
  57. data/spec/extensions/looser_typecasting_spec.rb +16 -19
  58. data/spec/extensions/migration_spec.rb +7 -1
  59. data/spec/extensions/serialization_spec.rb +22 -0
  60. data/spec/extensions/single_table_inheritance_spec.rb +3 -2
  61. data/spec/extensions/validation_helpers_spec.rb +6 -0
  62. data/spec/integration/dataset_test.rb +5 -0
  63. data/spec/integration/schema_test.rb +16 -0
  64. data/spec/model/associations_spec.rb +40 -0
  65. data/spec/model/base_spec.rb +21 -1
  66. data/spec/model/record_spec.rb +3 -0
  67. metadata +14 -10
@@ -8,7 +8,7 @@ Sequel uses autocommit mode by default for all of its database adapters, so in g
8
8
  * Migrations if the database supports transactional schema
9
9
  * A few model plugins
10
10
 
11
- Everywhere else, it is up to use to use a database transaction if you want to.
11
+ Everywhere else, it is up to you to use a database transaction if you want to.
12
12
 
13
13
  == Basic Transaction Usage
14
14
 
@@ -1,4 +1,5 @@
1
1
  Sequel.require 'adapters/jdbc/transactions'
2
+ Sequel.require 'adapters/utils/emulate_offset_with_row_number'
2
3
 
3
4
  module Sequel
4
5
  module JDBC
@@ -85,6 +85,12 @@ module Sequel
85
85
  sql = "ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(op[:name])} #{type_literal(op)}"
86
86
  column_definition_order.each{|m| send(:"column_definition_#{m}_sql", sql, op)}
87
87
  sql
88
+ when :drop_constraint
89
+ if op[:type] == :primary_key
90
+ "ALTER TABLE #{quote_schema_table(table)} DROP PRIMARY KEY"
91
+ else
92
+ super(table, op)
93
+ end
88
94
  else
89
95
  super(table, op)
90
96
  end
@@ -121,6 +127,11 @@ module Sequel
121
127
  PRIMARY_KEY_INDEX_RE
122
128
  end
123
129
 
130
+ # H2 does not support named column constraints.
131
+ def supports_named_column_constraints?
132
+ false
133
+ end
134
+
124
135
  # Use BIGINT IDENTITY for identity columns that use bigint, fixes
125
136
  # the case where primary_key :column, :type=>Bignum is used.
126
137
  def type_literal_generic_bignum(column)
@@ -27,17 +27,11 @@ module Sequel
27
27
  # a filter for an autoincrement column equals NULL to return the last
28
28
  # inserted row.
29
29
  # * :charset - Same as :encoding (:encoding takes precendence)
30
- # * :config_default_group - The default group to read from the in
31
- # the MySQL config file.
32
- # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
33
- # option on the connection with the given value.
34
- # * :connect_timeout - Set the timeout in seconds before a connection
35
- # attempt is abandoned.
36
30
  # * :encoding - Set all the related character sets for this
37
31
  # connection (connection, client, database, server, and results).
38
- # * :socket - Use a unix socket file instead of connecting via TCP/IP.
39
- # * :timeout - Set the timeout in seconds before the server will
40
- # disconnect this connection (a.k.a. @@wait_timeout).
32
+ #
33
+ # The options hash is also passed to mysql2, and can include mysql2
34
+ # options such as :local_infile.
41
35
  def connect(server)
42
36
  opts = server_opts(server)
43
37
  opts[:host] ||= 'localhost'
@@ -164,7 +164,7 @@ module Sequel
164
164
  # conversion is done, so an error is raised if you attempt to retrieve an infinite
165
165
  # timestamp. You can set this to :nil to convert to nil, :string to leave
166
166
  # as a string, or :float to convert to an infinite float.
167
- attr_accessor :convert_infinite_timestamps
167
+ attr_reader :convert_infinite_timestamps
168
168
 
169
169
  # Add the primary_keys and primary_key_sequences instance variables,
170
170
  # so we can get the correct return values for inserted rows.
@@ -233,6 +233,22 @@ module Sequel
233
233
  conn
234
234
  end
235
235
 
236
+ def convert_infinite_timestamps=(v)
237
+ @convert_infinite_timestamps = v
238
+ pr = old_pr = Postgres.use_iso_date_format ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
239
+ if v
240
+ pr = lambda do |v|
241
+ case v
242
+ when *INFINITE_TIMESTAMP_STRINGS
243
+ infinite_timestamp_value(v)
244
+ else
245
+ old_pr.call(v)
246
+ end
247
+ end
248
+ end
249
+ conversion_procs[1082] = pr
250
+ end
251
+
236
252
  # Disconnect given connection
237
253
  def disconnect_connection(conn)
238
254
  begin
@@ -396,7 +412,7 @@ module Sequel
396
412
  # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
397
413
  # value based on the convert_infinite_timestamps setting.
398
414
  def to_application_timestamp(value)
399
- if c = convert_infinite_timestamps
415
+ if convert_infinite_timestamps
400
416
  case value
401
417
  when *INFINITE_TIMESTAMP_STRINGS
402
418
  infinite_timestamp_value(value)
@@ -504,6 +520,22 @@ module Sequel
504
520
  conn.execute(sql)
505
521
  end
506
522
 
523
+ # If the value is an infinite value (either an infinite float or a string returned by
524
+ # by PostgreSQL for an infinite timestamp), return it without converting it if
525
+ # convert_infinite_timestamps is set.
526
+ def typecast_value_date(value)
527
+ if convert_infinite_timestamps
528
+ case value
529
+ when *INFINITE_DATETIME_VALUES
530
+ value
531
+ else
532
+ super
533
+ end
534
+ else
535
+ super
536
+ end
537
+ end
538
+
507
539
  # If the value is an infinite value (either an infinite float or a string returned by
508
540
  # by PostgreSQL for an infinite timestamp), return it without converting it if
509
541
  # convert_infinite_timestamps is set.
@@ -146,6 +146,11 @@ module Sequel
146
146
  nil
147
147
  end
148
148
 
149
+ # CUBRID does not support named column constraints.
150
+ def supports_named_column_constraints?
151
+ false
152
+ end
153
+
149
154
  # CUBRID doesn't support booleans, it recommends using smallint.
150
155
  def type_literal_generic_trueclass(column)
151
156
  :smallint
@@ -414,6 +414,7 @@ module Sequel
414
414
  APOS_RE = Dataset::APOS_RE
415
415
  DOUBLE_APOS = Dataset::DOUBLE_APOS
416
416
  INTO = Dataset::INTO
417
+ DOUBLE_BRACKET_CLOSE = ']]'.freeze
417
418
  DATEPART_SECOND_OPEN = "CAST((datepart(".freeze
418
419
  DATEPART_SECOND_MIDDLE = ') + datepart(ns, '.freeze
419
420
  DATEPART_SECOND_CLOSE = ")/1000000000.0) AS double precision)".freeze
@@ -434,6 +435,8 @@ module Sequel
434
435
  CASE_INSENSITIVE_COLLATION = 'Latin1_General_CI_AS'.freeze
435
436
  DEFAULT_TIMESTAMP_FORMAT = "'%Y-%m-%dT%H:%M:%S%N%z'".freeze
436
437
  FORMAT_DATE = "'%Y%m%d'".freeze
438
+ CROSS_APPLY = 'CROSS APPLY'.freeze
439
+ OUTER_APPLY = 'OUTER APPLY'.freeze
437
440
 
438
441
  Sequel::Dataset.def_mutation_method(:disable_insert_output, :output, :module=>self)
439
442
 
@@ -484,6 +487,11 @@ module Sequel
484
487
  end
485
488
  end
486
489
 
490
+ # Uses CROSS APPLY to join the given table into the current dataset.
491
+ def cross_apply(table)
492
+ join_table(:cross_apply, table)
493
+ end
494
+
487
495
  # Disable the use of INSERT OUTPUT
488
496
  def disable_insert_output
489
497
  clone(:disable_insert_output=>true)
@@ -545,6 +553,11 @@ module Sequel
545
553
  lock_style(:dirty)
546
554
  end
547
555
 
556
+ # Uses OUTER APPLY to join the given table into the current dataset.
557
+ def outer_apply(table)
558
+ join_table(:outer_apply, table)
559
+ end
560
+
548
561
  # Include an OUTPUT clause in the eventual INSERT, UPDATE, or DELETE query.
549
562
  #
550
563
  # The first argument is the table to output into, and the second argument
@@ -570,10 +583,9 @@ module Sequel
570
583
  clone({:output => output})
571
584
  end
572
585
 
573
- # MSSQL uses [] to quote identifiers. MSSQL does not support
574
- # escaping of ], so you cannot use that character in an identifier.
586
+ # MSSQL uses [] to quote identifiers.
575
587
  def quoted_identifier_append(sql, name)
576
- sql << BRACKET_OPEN << name.to_s << BRACKET_CLOSE
588
+ sql << BRACKET_OPEN << name.to_s.gsub(/\]/, DOUBLE_BRACKET_CLOSE) << BRACKET_CLOSE
577
589
  end
578
590
 
579
591
  # The version of the database server.
@@ -729,6 +741,18 @@ module Sequel
729
741
  end
730
742
  end
731
743
 
744
+ # Handle CROSS APPLY and OUTER APPLY JOIN types
745
+ def join_type_sql(join_type)
746
+ case join_type
747
+ when :cross_apply
748
+ CROSS_APPLY
749
+ when :outer_apply
750
+ OUTER_APPLY
751
+ else
752
+ super
753
+ end
754
+ end
755
+
732
756
  # MSSQL uses a literal hexidecimal number for blob strings
733
757
  def literal_blob_append(sql, v)
734
758
  sql << HEX_START << v.unpack(HSTAR).first
@@ -199,13 +199,16 @@ module Sequel
199
199
  unless op[:type] || opts[:type]
200
200
  raise Error, "cannot determine database type to use for CHANGE COLUMN operation"
201
201
  end
202
- "CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(op.merge(opts))}"
202
+ opts = op.merge(opts)
203
+ opts.delete(:auto_increment) if op[:auto_increment] == false
204
+ "CHANGE COLUMN #{quote_identifier(op[:name])} #{column_definition_sql(opts)}"
203
205
  when :drop_constraint
204
206
  type = case op[:type]
205
207
  when :primary_key
206
208
  "DROP PRIMARY KEY"
207
209
  when :foreign_key
208
- "DROP FOREIGN KEY #{quote_identifier(op[:name])}"
210
+ name = op[:name] || foreign_key_name(table, op[:columns])
211
+ "DROP FOREIGN KEY #{quote_identifier(name)}"
209
212
  when :unique
210
213
  "DROP INDEX #{quote_identifier(op[:name])}"
211
214
  end
@@ -224,6 +227,13 @@ module Sequel
224
227
  case op[:op]
225
228
  when :drop_index
226
229
  "#{drop_index_sql(table, op)} ON #{quote_schema_table(table)}"
230
+ when :drop_constraint
231
+ if op[:type] == :primary_key
232
+ if (pk = primary_key_from_schema(table)).length == 1
233
+ return [alter_table_sql(table, {:op=>:rename_column, :name=>pk.first, :new_name=>pk.first, :auto_increment=>false}), super]
234
+ end
235
+ end
236
+ super
227
237
  else
228
238
  super
229
239
  end
@@ -242,7 +252,7 @@ module Sequel
242
252
  # operations, since in some cases adding a foreign key constraint in
243
253
  # the same query as other operations results in MySQL error 150.
244
254
  def combinable_alter_table_op?(op)
245
- super && !(op[:op] == :add_constraint && op[:type] == :foreign_key)
255
+ super && !(op[:op] == :add_constraint && op[:type] == :foreign_key) && !(op[:op] == :drop_constraint && op[:type] == :primary_key)
246
256
  end
247
257
 
248
258
  # The SQL queries to execute on initial connection
@@ -317,6 +327,7 @@ module Sequel
317
327
  collate = options.fetch(:collate, Sequel::MySQL.default_collate)
318
328
  generator.constraints.sort_by{|c| (c[:type] == :primary_key) ? -1 : 1}
319
329
 
330
+ # Proc for figuring out the primary key for a given table.
320
331
  key_proc = lambda do |t|
321
332
  if t == name
322
333
  if pk = generator.primary_key_name
@@ -329,12 +340,17 @@ module Sequel
329
340
  end
330
341
  end
331
342
 
343
+ # Manually set the keys, since MySQL requires one, it doesn't use the primary
344
+ # key if none are specified.
332
345
  generator.constraints.each do |c|
333
346
  if c[:type] == :foreign_key
334
347
  c[:key] ||= key_proc.call(c[:table])
335
348
  end
336
349
  end
337
350
 
351
+ # Split column constraints into table constraints in some cases:
352
+ # * foreign key - Always
353
+ # * unique, primary_key - Only if constraint has a name
338
354
  generator.columns.each do |c|
339
355
  if t = c.delete(:table)
340
356
  same_table = t == name
@@ -346,7 +362,7 @@ module Sequel
346
362
  generator.constraints.unshift(:type=>:unique, :columns=>Array(k))
347
363
  end
348
364
 
349
- generator.foreign_key([c[:name]], t, c.merge(:name=>nil, :type=>:foreign_key, :key=>key))
365
+ generator.foreign_key([c[:name]], t, c.merge(:name=>c[:foreign_key_constraint_name], :type=>:foreign_key, :key=>key))
350
366
  end
351
367
  end
352
368
 
@@ -448,6 +464,11 @@ module Sequel
448
464
  true
449
465
  end
450
466
 
467
+ # MySQL does not support named column constraints.
468
+ def supports_named_column_constraints?
469
+ false
470
+ end
471
+
451
472
  # Respect the :size option if given to produce
452
473
  # tinyblob, mediumblob, and longblob if :tiny,
453
474
  # :medium, or :long is given.
@@ -221,6 +221,7 @@ module Sequel
221
221
  ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
222
222
  end
223
223
  end
224
+ ensure
224
225
  self.foreign_keys = true if fks
225
226
  end
226
227
 
@@ -255,7 +256,11 @@ module Sequel
255
256
  when :primary_key
256
257
  duplicate_table(table){|columns| columns.each{|s| s[:primary_key] = nil}}
257
258
  when :foreign_key
258
- duplicate_table(table, :no_foreign_keys=>true)
259
+ if op[:columns]
260
+ duplicate_table(table, :skip_foreign_key_columns=>op[:columns])
261
+ else
262
+ duplicate_table(table, :no_foreign_keys=>true)
263
+ end
259
264
  else
260
265
  duplicate_table(table)
261
266
  end
@@ -366,6 +371,12 @@ module Sequel
366
371
  if ocp = opts[:old_columns_proc]
367
372
  fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
368
373
  end
374
+
375
+ # Skip any foreign key columns where a constraint for those
376
+ # foreign keys is being dropped.
377
+ if sfkc = opts[:skip_foreign_key_columns]
378
+ fks.delete_if{|c| c[:columns] == sfkc}
379
+ end
369
380
 
370
381
  constraints.concat(fks.each{|h| h[:type] = :foreign_key})
371
382
  end
@@ -18,8 +18,8 @@
18
18
  #
19
19
  # add_servers(Array of Symbols) :: start recognizing all shards/servers specified
20
20
  # by the array of symbols.
21
- # * remove_servers(Array of Symbols) :: no longer recognize all shards/servers
22
- # specified by the array of symbols.
21
+ # remove_servers(Array of Symbols) :: no longer recognize all shards/servers
22
+ # specified by the array of symbols.
23
23
  class Sequel::ConnectionPool
24
24
  # The default server to use
25
25
  DEFAULT_SERVER = :default
@@ -42,7 +42,7 @@ class Sequel::ConnectionPool
42
42
  when Class
43
43
  v.new(db, opts)
44
44
  when Symbol
45
- Sequel.ts_require("connection_pool/#{v}")
45
+ Sequel.tsk_require("sequel/connection_pool/#{v}")
46
46
  connection_pool_class(opts).new(db, opts) || raise(Sequel::Error, "No connection pool class found")
47
47
  end
48
48
  end
@@ -185,13 +185,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
185
185
  # returns the connection. The calling code should already have the mutex
186
186
  # before calling this.
187
187
  def checkin_connection(server, conn)
188
- case @connection_handling
189
- when :queue
190
- available_connections(server).unshift(conn)
191
- else
192
- available_connections(server) << conn
193
- end
194
-
188
+ available_connections(server) << conn
195
189
  conn
196
190
  end
197
191
 
@@ -222,7 +216,12 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
222
216
  # if there is not currently an available connection for the server.
223
217
  # The calling code should already have the mutex before calling this.
224
218
  def next_available(server)
225
- available_connections(server).pop
219
+ case @connection_handling
220
+ when :queue
221
+ available_connections(server).shift
222
+ else
223
+ available_connections(server).pop
224
+ end
226
225
  end
227
226
 
228
227
  # Returns the connection owned by the supplied thread for the given server,
@@ -140,13 +140,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
140
140
  # Return a connection to the pool of available connections, returns the connection.
141
141
  # The calling code should already have the mutex before calling this.
142
142
  def checkin_connection(conn)
143
- case @connection_handling
144
- when :queue
145
- @available_connections.unshift(conn)
146
- else
147
- @available_connections << conn
148
- end
149
-
143
+ @available_connections << conn
150
144
  conn
151
145
  end
152
146
 
@@ -168,7 +162,12 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
168
162
  # is not currently an available connection. The calling code should already
169
163
  # have the mutex before calling this.
170
164
  def next_available
171
- @available_connections.pop
165
+ case @connection_handling
166
+ when :queue
167
+ @available_connections.shift
168
+ else
169
+ @available_connections.pop
170
+ end
172
171
  end
173
172
 
174
173
  # Returns the connection owned by the supplied thread,
@@ -20,7 +20,7 @@
20
20
  # Sequel currently adds methods to the Array, Hash, String and Symbol classes by
21
21
  # default. You can either require 'sequel/no_core_ext' or set the
22
22
  # +SEQUEL_NO_CORE_EXTENSIONS+ constant or environment variable before requiring
23
- # sequel to have # Sequel not add methods to those classes.
23
+ # sequel to have Sequel not add methods to those classes.
24
24
  #
25
25
  # For a more expanded introduction, see the {README}[link:files/README_rdoc.html].
26
26
  # For a quicker introduction, see the {cheat sheet}[link:files/doc/cheat_sheet_rdoc.html].
@@ -332,7 +332,7 @@ module Sequel
332
332
  else
333
333
  # Yield directly to the block. You don't need to synchronize
334
334
  # access on MRI because the GVL makes certain methods atomic.
335
- def self.synchronize(&block)
335
+ def self.synchronize
336
336
  yield
337
337
  end
338
338
  end
@@ -422,6 +422,9 @@ module Sequel
422
422
 
423
423
  # Method that adds a database adapter class method to Sequel that calls
424
424
  # Sequel.adapter_method.
425
+ #
426
+ # Do not call this method with untrusted input, as that can result in
427
+ # arbitrary code execution.
425
428
  def self.def_adapter_method(*adapters) # :nodoc:
426
429
  adapters.each do |adapter|
427
430
  instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end", __FILE__, __LINE__)
@@ -15,5 +15,5 @@ module Sequel
15
15
  class Database
16
16
  end
17
17
 
18
- require(%w"connecting dataset dataset_defaults logging misc query schema_generator schema_methods", 'database')
18
+ require(%w"connecting dataset dataset_defaults logging features misc query transactions schema_generator schema_methods", 'database')
19
19
  end
@@ -56,10 +56,7 @@ module Sequel
56
56
  uri_options = c.send(:uri_to_options, uri)
57
57
  uri.query.split('&').collect{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
58
58
  uri_options.to_a.each{|k,v| uri_options[k] = URI.unescape(v) if v.is_a?(String)}
59
- opts = opts.merge(:orig_opts=>opts.dup)
60
- opts[:uri] = conn_string
61
- opts = uri_options.merge(opts)
62
- opts[:adapter] = scheme
59
+ opts = uri_options.merge(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
63
60
  end
64
61
  when Hash
65
62
  opts = conn_string.merge(opts)
@@ -77,14 +74,16 @@ module Sequel
77
74
  begin
78
75
  db = c.new(opts)
79
76
  db.test_connection if opts[:test] && db.send(:typecast_value_boolean, opts[:test])
80
- result = yield(db) if block_given?
77
+ if block_given?
78
+ return yield(db)
79
+ end
81
80
  ensure
82
81
  if block_given?
83
82
  db.disconnect if db
84
83
  Sequel.synchronize{::Sequel::DATABASES.delete(db)}
85
84
  end
86
85
  end
87
- block_given? ? result : db
86
+ db
88
87
  end
89
88
 
90
89
  # Sets the default single_threaded mode for new databases.
@@ -107,7 +106,7 @@ module Sequel
107
106
  # Sequel.connect('mydb://user:password@dbserver/mydb')
108
107
  def self.set_adapter_scheme(scheme) # :nodoc:
109
108
  @scheme = scheme
110
- ADAPTER_MAP[scheme.to_sym] = self
109
+ ADAPTER_MAP[scheme] = self
111
110
  end
112
111
  private_class_method :set_adapter_scheme
113
112
 
@@ -186,6 +185,7 @@ module Sequel
186
185
  #
187
186
  # DB.each_server{|db| db.create_table(:users){primary_key :id; String :name}}
188
187
  def each_server(&block)
188
+ raise(Error, "Database#each_server must be passed a block") unless block
189
189
  servers.each{|s| self.class.connect(server_opts(s), &block)}
190
190
  end
191
191