sequel 3.45.0 → 3.46.0

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