sequel 3.32.0 → 3.33.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 (58) hide show
  1. data/CHANGELOG +42 -0
  2. data/Rakefile +3 -2
  3. data/doc/migration.rdoc +41 -14
  4. data/doc/opening_databases.rdoc +2 -0
  5. data/doc/release_notes/3.29.0.txt +1 -1
  6. data/doc/release_notes/3.33.0.txt +157 -0
  7. data/doc/sharding.rdoc +93 -7
  8. data/doc/testing.rdoc +1 -1
  9. data/lib/sequel/adapters/do.rb +1 -0
  10. data/lib/sequel/adapters/do/mysql.rb +6 -0
  11. data/lib/sequel/adapters/jdbc.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  13. data/lib/sequel/adapters/mock.rb +10 -5
  14. data/lib/sequel/adapters/mysql.rb +23 -1
  15. data/lib/sequel/adapters/mysql2.rb +16 -2
  16. data/lib/sequel/adapters/postgres.rb +22 -4
  17. data/lib/sequel/adapters/shared/mysql.rb +51 -10
  18. data/lib/sequel/adapters/shared/postgres.rb +101 -63
  19. data/lib/sequel/adapters/shared/sqlite.rb +19 -0
  20. data/lib/sequel/adapters/sqlite.rb +71 -16
  21. data/lib/sequel/adapters/swift.rb +1 -0
  22. data/lib/sequel/connection_pool.rb +1 -1
  23. data/lib/sequel/connection_pool/sharded_single.rb +6 -1
  24. data/lib/sequel/connection_pool/sharded_threaded.rb +6 -1
  25. data/lib/sequel/connection_pool/threaded.rb +12 -11
  26. data/lib/sequel/database/connecting.rb +2 -0
  27. data/lib/sequel/database/misc.rb +6 -0
  28. data/lib/sequel/database/query.rb +1 -1
  29. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  30. data/lib/sequel/extensions/migration.rb +45 -7
  31. data/lib/sequel/extensions/server_block.rb +139 -0
  32. data/lib/sequel/model/associations.rb +9 -9
  33. data/lib/sequel/model/inflections.rb +1 -1
  34. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  35. data/lib/sequel/plugins/json_serializer.rb +1 -1
  36. data/lib/sequel/plugins/list.rb +12 -2
  37. data/lib/sequel/version.rb +1 -1
  38. data/spec/adapters/mysql_spec.rb +64 -8
  39. data/spec/adapters/postgres_spec.rb +139 -78
  40. data/spec/adapters/sqlite_spec.rb +87 -0
  41. data/spec/core/connection_pool_spec.rb +14 -0
  42. data/spec/core/database_spec.rb +5 -0
  43. data/spec/core/mock_adapter_spec.rb +21 -9
  44. data/spec/extensions/arbitrary_servers_spec.rb +114 -0
  45. data/spec/extensions/instance_hooks_spec.rb +19 -0
  46. data/spec/extensions/list_spec.rb +31 -0
  47. data/spec/extensions/migration_spec.rb +61 -4
  48. data/spec/extensions/server_block_spec.rb +90 -0
  49. data/spec/extensions/spec_helper.rb +1 -1
  50. data/spec/files/transaction_migrations/001_create_alt_basic.rb +3 -0
  51. data/spec/files/transaction_migrations/002_create_basic.rb +3 -0
  52. data/spec/files/transactionless_migrations/001_create_alt_basic.rb +4 -0
  53. data/spec/files/transactionless_migrations/002_create_basic.rb +4 -0
  54. data/spec/integration/dataset_test.rb +2 -2
  55. data/spec/integration/plugin_test.rb +9 -9
  56. data/spec/integration/schema_test.rb +3 -1
  57. data/spec/integration/transaction_test.rb +2 -2
  58. metadata +12 -2
@@ -11,6 +11,10 @@ module Sequel
11
11
  TEMP_STORE = [:default, :file, :memory].freeze
12
12
  VIEWS_FILTER = "type = 'view'".freeze
13
13
 
14
+ # Whether to use integers for booleans in the database. SQLite recommends
15
+ # booleans be stored as integers, but historically Sequel has used 't'/'f'.
16
+ attr_accessor :integer_booleans
17
+
14
18
  # A symbol signifying the value of the auto_vacuum PRAGMA.
15
19
  def auto_vacuum
16
20
  AUTO_VACUUM[pragma_get(:auto_vacuum).to_i]
@@ -89,6 +93,11 @@ module Sequel
89
93
  def pragma_set(name, value)
90
94
  execute_ddl("PRAGMA #{name} = #{value}")
91
95
  end
96
+
97
+ # Set the integer_booleans option using the passed in :integer_boolean option.
98
+ def set_integer_booleans
99
+ @integer_booleans = typecast_value_boolean(@opts[:integer_booleans])
100
+ end
92
101
 
93
102
  # The version of the server as an integer, where 3.6.19 = 30619.
94
103
  # If the server version can't be determined, 0 is used.
@@ -557,6 +566,16 @@ module Sequel
557
566
  sql << BLOB_START << v.unpack(HSTAR).first << APOS
558
567
  end
559
568
 
569
+ # Respect the database integer_booleans setting, using 0 or 'f'.
570
+ def literal_false
571
+ @db.integer_booleans ? '0' : "'f'"
572
+ end
573
+
574
+ # Respect the database integer_booleans setting, using 1 or 't'.
575
+ def literal_true
576
+ @db.integer_booleans ? '1' : "'t'"
577
+ end
578
+
560
579
  # SQLite does not support the SQL WITH clause
561
580
  def select_clause_methods
562
581
  SELECT_CLAUSE_METHODS
@@ -1,9 +1,4 @@
1
1
  require 'sqlite3'
2
- begin
3
- SQLite3::Database.instance_method(:type_translation)
4
- rescue
5
- raise(Sequel::Error, "SQLite3::Database#type_translation is not defined. If you are using the sqlite3 gem, please install the sqlite3-ruby gem.")
6
- end
7
2
  Sequel.require 'adapters/shared/sqlite'
8
3
 
9
4
  module Sequel
@@ -11,23 +6,69 @@ module Sequel
11
6
  # for Sequel.
12
7
  module SQLite
13
8
  TYPE_TRANSLATOR = tt = Class.new do
14
- FALSE_VALUES = %w'0 false f no n'.freeze
15
- def boolean(s) !FALSE_VALUES.include?(s.downcase) end
16
- def integer(s) s.to_i end
17
- def float(s) s.to_f end
18
- def numeric(s) ::BigDecimal.new(s) rescue s end
9
+ FALSE_VALUES = (%w'0 false f no n' + [0]).freeze
10
+
11
+ def blob(s)
12
+ Sequel::SQL::Blob.new(s.to_s)
13
+ end
14
+
15
+ def boolean(s)
16
+ s = s.downcase if s.is_a?(String)
17
+ !FALSE_VALUES.include?(s)
18
+ end
19
+
20
+ def date(s)
21
+ case s
22
+ when String
23
+ Sequel.string_to_date(s)
24
+ when Integer
25
+ Date.jd(s)
26
+ when Float
27
+ Date.jd(s.to_i)
28
+ else
29
+ raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
30
+ end
31
+ end
32
+
33
+ def integer(s)
34
+ s.to_i
35
+ end
36
+
37
+ def float(s)
38
+ s.to_f
39
+ end
40
+
41
+ def numeric(s)
42
+ s = s.to_s unless s.is_a?(String)
43
+ ::BigDecimal.new(s) rescue s
44
+ end
45
+
46
+ def time(s)
47
+ case s
48
+ when String
49
+ Sequel.string_to_time(s)
50
+ when Integer
51
+ Sequel::SQLTime.create(s/3600, (s % 3600)/60, s % 60)
52
+ when Float
53
+ s, f = s.divmod(1)
54
+ Sequel::SQLTime.create(s/3600, (s % 3600)/60, s % 60, (f*1000000).round)
55
+ else
56
+ raise Sequel::Error, "unhandled type when converting to date: #{s.inspect} (#{s.class.inspect})"
57
+ end
58
+ end
59
+
19
60
  end.new
20
61
 
21
62
  # Hash with string keys and callable values for converting SQLite types.
22
63
  SQLITE_TYPES = {}
23
64
  {
24
- %w'date' => ::Sequel.method(:string_to_date),
25
- %w'time' => ::Sequel.method(:string_to_time),
65
+ %w'date' => tt.method(:date),
66
+ %w'time' => tt.method(:time),
26
67
  %w'bit bool boolean' => tt.method(:boolean),
27
68
  %w'integer smallint mediumint int bigint' => tt.method(:integer),
28
69
  %w'numeric decimal money' => tt.method(:numeric),
29
70
  %w'float double real dec fixed' + ['double precision'] => tt.method(:float),
30
- %w'blob' => ::Sequel::SQL::Blob.method(:new)
71
+ %w'blob' => tt.method(:blob)
31
72
  }.each do |k,v|
32
73
  k.each{|n| SQLITE_TYPES[n] = v}
33
74
  end
@@ -53,8 +94,8 @@ module Sequel
53
94
  def initialize(opts={})
54
95
  super
55
96
  @conversion_procs = SQLITE_TYPES.dup
56
- @conversion_procs['timestamp'] = method(:to_application_timestamp)
57
- @conversion_procs['datetime'] = method(:to_application_timestamp)
97
+ @conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
98
+ set_integer_booleans
58
99
  end
59
100
 
60
101
  # Connect to the database. Since SQLite is a file based database,
@@ -108,6 +149,20 @@ module Sequel
108
149
  _execute(:single_value, sql, opts)
109
150
  end
110
151
 
152
+ # Handle Integer and Float arguments, since SQLite can store timestamps as integers and floats.
153
+ def to_application_timestamp(s)
154
+ case s
155
+ when String
156
+ super
157
+ when Integer
158
+ super(Time.at(s).to_s)
159
+ when Float
160
+ super(DateTime.jd(s).to_s)
161
+ else
162
+ raise Sequel::Error, "unhandled type when converting to : #{s.inspect} (#{s.class.inspect})"
163
+ end
164
+ end
165
+
111
166
  private
112
167
 
113
168
  # Yield an available connection. Rescue
@@ -303,7 +358,7 @@ module Sequel
303
358
  row = {}
304
359
  cols.each do |name,i,type_proc|
305
360
  v = values[i]
306
- if type_proc && v.is_a?(String)
361
+ if type_proc && v
307
362
  v = type_proc.call(v)
308
363
  end
309
364
  row[name] = v
@@ -29,6 +29,7 @@ module Sequel
29
29
  db.extend(Sequel::Swift::SQLite::DatabaseMethods)
30
30
  db.dataset_class = Sequel::Swift::SQLite::Dataset
31
31
  db.swift_class = ::Swift::DB::Sqlite3
32
+ db.set_integer_booleans
32
33
  end,
33
34
  }
34
35
 
@@ -80,7 +80,7 @@ class Sequel::ConnectionPool
80
80
 
81
81
  # An array of symbols for all shards/servers, which is a single <tt>:default</tt> by default.
82
82
  def servers
83
- [:default]
83
+ [DEFAULT_SERVER]
84
84
  end
85
85
 
86
86
  private
@@ -45,7 +45,7 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
45
45
  # This method simulates the ConnectionPool#hold API.
46
46
  def hold(server=:default)
47
47
  begin
48
- server = @servers[server]
48
+ server = pick_server(server)
49
49
  yield(@conns[server] ||= make_new(server))
50
50
  rescue Sequel::DatabaseDisconnectError
51
51
  disconnect_server(server, &@disconnection_proc)
@@ -83,6 +83,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
83
83
  block.call(conn) if block
84
84
  end
85
85
  end
86
+
87
+ # If the server given is in the hash, return it, otherwise, return the default server.
88
+ def pick_server(server)
89
+ @servers[server]
90
+ end
86
91
 
87
92
  CONNECTION_POOL_MAP[[true, true]] = self
88
93
  end
@@ -94,7 +94,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
94
94
  # connection can be acquired, a Sequel::PoolTimeout is
95
95
  # raised.
96
96
  def hold(server=:default)
97
- sync{server = @servers[server]}
97
+ server = pick_server(server)
98
98
  t = Thread.current
99
99
  if conn = owned_connection(t, server)
100
100
  return yield(conn)
@@ -190,6 +190,11 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
190
190
  def owned_connection(thread, server)
191
191
  sync{@allocated[server][thread]}
192
192
  end
193
+
194
+ # If the server given is in the hash, return it, otherwise, return the default server.
195
+ def pick_server(server)
196
+ sync{@servers[server]}
197
+ end
193
198
 
194
199
  # Releases the connection assigned to the supplied thread and server. If the
195
200
  # server or connection given is scheduled for disconnection, remove the
@@ -30,16 +30,19 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
30
30
  @sleep_time = Float(opts[:pool_sleep_time] || 0.001)
31
31
  end
32
32
 
33
- # The total number of connections opened for the given server, should
34
- # be equal to available_connections.length + allocated.length.
33
+ # The total number of connections opened, either available or allocated.
34
+ # This may not be completely accurate as it isn't protected by the mutex.
35
35
  def size
36
36
  @allocated.length + @available_connections.length
37
37
  end
38
38
 
39
- # Removes all connections currently available on all servers, optionally
39
+ # Removes all connections currently available, optionally
40
40
  # yielding each connection to the given block. This method has the effect of
41
41
  # disconnecting from the database, assuming that no connections are currently
42
- # being used.
42
+ # being used. If you want to be able to disconnect connections that are
43
+ # currently in use, use the ShardedThreadedConnectionPool, which can do that.
44
+ # This connection pool does not, for performance reasons. To use the sharded pool,
45
+ # pass the <tt>:servers=>{}</tt> option when connecting to the database.
43
46
  #
44
47
  # Once a connection is requested using #hold, the connection pool
45
48
  # creates new connections to the database.
@@ -51,7 +54,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
51
54
  end
52
55
  end
53
56
 
54
- # Chooses the first available connection to the given server, or if none are
57
+ # Chooses the first available connection, or if none are
55
58
  # available, creates a new connection. Passes the connection to the supplied
56
59
  # block:
57
60
  #
@@ -95,7 +98,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
95
98
 
96
99
  private
97
100
 
98
- # Assigns a connection to the supplied thread for the given server, if one
101
+ # Assigns a connection to the supplied thread, if one
99
102
  # is available. The calling code should NOT already have the mutex when
100
103
  # calling this.
101
104
  def acquire(thread)
@@ -106,7 +109,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
106
109
  end
107
110
  end
108
111
 
109
- # Returns an available connection to the given server. If no connection is
112
+ # Returns an available connection. If no connection is
110
113
  # available, tries to create a new connection. The calling code should already
111
114
  # have the mutex before calling this.
112
115
  def available
@@ -127,15 +130,13 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
127
130
  super if (n || size) < @max_size
128
131
  end
129
132
 
130
- # Returns the connection owned by the supplied thread for the given server,
133
+ # Returns the connection owned by the supplied thread,
131
134
  # if any. The calling code should NOT already have the mutex before calling this.
132
135
  def owned_connection(thread)
133
136
  sync{@allocated[thread]}
134
137
  end
135
138
 
136
- # Releases the connection assigned to the supplied thread and server. If the
137
- # server or connection given is scheduled for disconnection, remove the
138
- # connection instead of releasing it back to the pool.
139
+ # Releases the connection assigned to the supplied thread back to the pool.
139
140
  # The calling code should already have the mutex before calling this.
140
141
  def release(thread)
141
142
  @available_connections << @allocated.delete(thread)
@@ -254,6 +254,8 @@ module Sequel
254
254
  else
255
255
  raise Error, 'Server opts should be a hash or proc'
256
256
  end
257
+ elsif server.is_a?(Hash)
258
+ @opts.merge(server)
257
259
  else
258
260
  @opts.dup
259
261
  end
@@ -159,6 +159,12 @@ module Sequel
159
159
  false
160
160
  end
161
161
 
162
+ # Whether the database and adapter support savepoints inside prepared transactions
163
+ # (two-phase commit), default is false.
164
+ def supports_savepoints_in_prepared_transactions?
165
+ supports_prepared_transactions? && supports_savepoints?
166
+ end
167
+
162
168
  # Whether the database and adapter support transaction isolation levels, false by default.
163
169
  def supports_transaction_isolation_levels?
164
170
  false
@@ -39,7 +39,7 @@ module Sequel
39
39
  # Runs the supplied SQL statement string on the database server.
40
40
  # Returns self so it can be safely chained:
41
41
  #
42
- # DB << "UPADTE albums SET artist_id = NULL" << "DROP TABLE artists"
42
+ # DB << "UPDATE albums SET artist_id = NULL" << "DROP TABLE artists"
43
43
  def <<(sql)
44
44
  run(sql)
45
45
  self
@@ -0,0 +1,108 @@
1
+ # The arbitrary_servers extension allows you to connect to arbitrary
2
+ # servers/shards that were not defined when you created the database.
3
+ # To use it, you first extend the Database's connection pool with the
4
+ # Sequel::ArbitraryServers module:
5
+ #
6
+ # Sequel.extension :arbitrary_servers
7
+ # DB.pool.extend Sequel::ArbitraryServers
8
+ #
9
+ # Then you can pass arbitrary connection options for the server/shard
10
+ # to use as a hash:
11
+ #
12
+ # DB[:table].server(:host=>'...', :database=>'...').all
13
+ #
14
+ # Because Sequel can never be sure that the connection will be reused,
15
+ # arbitrary connections are disconnected as soon as the outermost block
16
+ # that uses them exits. So this example uses the same connection:
17
+ #
18
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c|
19
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c2|
20
+ # # c == c2
21
+ # end
22
+ # end
23
+ #
24
+ # But this example does not:
25
+ #
26
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c|
27
+ # end
28
+ # DB.transaction(:server=>{:host=>'...', :database=>'...'}) do |c2|
29
+ # # c != c2
30
+ # end
31
+ #
32
+ # You can use this extension in conjunction with the server_block
33
+ # extension:
34
+ #
35
+ # DB.with_server(:host=>'...', :database=>'...') do
36
+ # DB.synchronize do
37
+ # # All of these use the host/database given to with_server
38
+ # DB[:table].insert(...)
39
+ # DB[:table].update(...)
40
+ # DB.tables
41
+ # DB[:table].all
42
+ # end
43
+ # end
44
+ #
45
+ # Anyone using this extension in conjunction with the server_block
46
+ # extension may want to do the following to so that you don't need
47
+ # to call synchronize separately:
48
+ #
49
+ # def DB.with_server(*)
50
+ # super{synchronize{yield}}
51
+ # end
52
+ #
53
+ # Note that this extension only works with the sharded threaded connection
54
+ # pool. If you are using the sharded single connection pool, you need
55
+ # to switch to the sharded threaded connection pool before using this
56
+ # extension.
57
+
58
+ module Sequel
59
+ module ArbitraryServers
60
+ private
61
+
62
+ # If server is a hash, create a new connection for
63
+ # it, and cache it first by thread and then server.
64
+ def acquire(thread, server)
65
+ if server.is_a?(Hash)
66
+ sync{@allocated[thread] ||= {}}[server] = make_new(server)
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ # If server is a hash, the entry for it probably doesn't
73
+ # exist in the @allocated hash, so check for existence to
74
+ # avoid calling nil.[]
75
+ def owned_connection(thread, server)
76
+ if server.is_a?(Hash)
77
+ if a = sync{@allocated[thread]}
78
+ a[server]
79
+ end
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ # If server is a hash, return it directly.
86
+ def pick_server(server)
87
+ if server.is_a?(Hash)
88
+ server
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ # If server is a hash, delete the thread from the allocated
95
+ # connections for that server. Additionally, if this was the last thread
96
+ # using that server, delete the server from the @allocated hash.
97
+ def release(thread, conn, server)
98
+ if server.is_a?(Hash)
99
+ a = @allocated[thread]
100
+ a.delete(server)
101
+ @allocated.delete(thread) if a.empty?
102
+ @disconnection_proc.call(conn) if @disconnection_proc
103
+ else
104
+ super
105
+ end
106
+ end
107
+ end
108
+ end
@@ -22,7 +22,7 @@ module Sequel
22
22
  #
23
23
  # Part of the +migration+ extension.
24
24
  class Migration
25
- # Creates a new instance of the migration and sets the @db attribute.
25
+ # Set the database associated with this migration.
26
26
  def initialize(db)
27
27
  @db = db
28
28
  end
@@ -43,6 +43,11 @@ module Sequel
43
43
  def self.inherited(base)
44
44
  descendants << base
45
45
  end
46
+
47
+ # Always use transactions for old-style migrations.
48
+ def self.use_transactions
49
+ true
50
+ end
46
51
 
47
52
  # The default down action does nothing
48
53
  def down
@@ -69,6 +74,14 @@ module Sequel
69
74
  # Proc used for the up action
70
75
  attr_accessor :up
71
76
 
77
+ # Whether to use transactions for this migration, true by default.
78
+ attr_accessor :use_transactions
79
+
80
+ # Use transactions by default
81
+ def initialize
82
+ @use_transactions = true
83
+ end
84
+
72
85
  # Apply the appropriate block on the +Database+
73
86
  # instance using instance_eval.
74
87
  def apply(db, direction)
@@ -100,6 +113,11 @@ module Sequel
100
113
  migration.down = block
101
114
  end
102
115
 
116
+ # Disable the use of transactions for the related migration
117
+ def no_transaction
118
+ migration.use_transactions = false
119
+ end
120
+
103
121
  # Defines the migration's up action.
104
122
  def up(&block)
105
123
  migration.up = block
@@ -337,11 +355,15 @@ module Sequel
337
355
  # if the version number appears to be a unix time integer for a year
338
356
  # after 2005, otherwise uses the IntegerMigrator.
339
357
  def self.migrator_class(directory)
340
- Dir.new(directory).each do |file|
341
- next unless MIGRATION_FILE_PATTERN.match(file)
342
- return TimestampMigrator if file.split(MIGRATION_SPLITTER, 2).first.to_i > MINIMUM_TIMESTAMP
358
+ if self.equal?(Migrator)
359
+ Dir.new(directory).each do |file|
360
+ next unless MIGRATION_FILE_PATTERN.match(file)
361
+ return TimestampMigrator if file.split(MIGRATION_SPLITTER, 2).first.to_i > MINIMUM_TIMESTAMP
362
+ end
363
+ IntegerMigrator
364
+ else
365
+ self
343
366
  end
344
- IntegerMigrator
345
367
  end
346
368
  private_class_method :migrator_class
347
369
 
@@ -380,10 +402,23 @@ module Sequel
380
402
  @table = schema ? Sequel::SQL::QualifiedIdentifier.new(schema, table) : table
381
403
  @column = opts[:column] || self.class.const_get(:DEFAULT_SCHEMA_COLUMN)
382
404
  @ds = schema_dataset
405
+ @use_transactions = opts[:use_transactions]
383
406
  end
384
407
 
385
408
  private
386
409
 
410
+ # If transactions should be used for the migration, yield to the block
411
+ # inside a transaction. Otherwise, just yield to the block.
412
+ def checked_transaction(migration, &block)
413
+ if @use_transactions == false
414
+ yield
415
+ elsif migration.use_transactions || @use_transactions
416
+ db.transaction(&block)
417
+ else
418
+ yield
419
+ end
420
+ end
421
+
387
422
  # Remove all migration classes. Done by the migrator to ensure that
388
423
  # the correct migration classes are picked up.
389
424
  def remove_migration_classes
@@ -437,7 +472,7 @@ module Sequel
437
472
  t = Time.now
438
473
  lv = up? ? v : v + 1
439
474
  db.log_info("Begin applying migration version #{lv}, direction: #{direction}")
440
- db.transaction do
475
+ checked_transaction(m) do
441
476
  m.apply(db, direction)
442
477
  set_migration_version(v)
443
478
  end
@@ -461,6 +496,9 @@ module Sequel
461
496
  Dir.new(directory).each do |file|
462
497
  next unless MIGRATION_FILE_PATTERN.match(file)
463
498
  version = migration_version_from_file(file)
499
+ if version >= 20000101
500
+ raise Migrator::Error, "Migration number too large, must use TimestampMigrator: #{file}"
501
+ end
464
502
  raise(Error, "Duplicate migration version: #{version}") if files[version]
465
503
  files[version] = File.join(directory, file)
466
504
  end
@@ -550,7 +588,7 @@ module Sequel
550
588
  migration_tuples.each do |m, f, direction|
551
589
  t = Time.now
552
590
  db.log_info("Begin applying migration #{f}, direction: #{direction}")
553
- db.transaction do
591
+ checked_transaction(m) do
554
592
  m.apply(db, direction)
555
593
  fi = f.downcase
556
594
  direction == :up ? ds.insert(column=>fi) : ds.filter(column=>fi).delete