sequel 3.32.0 → 3.33.0

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