sequel 3.32.0 → 3.33.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/Rakefile +3 -2
- data/doc/migration.rdoc +41 -14
- data/doc/opening_databases.rdoc +2 -0
- data/doc/release_notes/3.29.0.txt +1 -1
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/sharding.rdoc +93 -7
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/do.rb +1 -0
- data/lib/sequel/adapters/do/mysql.rb +6 -0
- data/lib/sequel/adapters/jdbc.rb +1 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
- data/lib/sequel/adapters/mock.rb +10 -5
- data/lib/sequel/adapters/mysql.rb +23 -1
- data/lib/sequel/adapters/mysql2.rb +16 -2
- data/lib/sequel/adapters/postgres.rb +22 -4
- data/lib/sequel/adapters/shared/mysql.rb +51 -10
- data/lib/sequel/adapters/shared/postgres.rb +101 -63
- data/lib/sequel/adapters/shared/sqlite.rb +19 -0
- data/lib/sequel/adapters/sqlite.rb +71 -16
- data/lib/sequel/adapters/swift.rb +1 -0
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/connection_pool/sharded_single.rb +6 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +6 -1
- data/lib/sequel/connection_pool/threaded.rb +12 -11
- data/lib/sequel/database/connecting.rb +2 -0
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/migration.rb +45 -7
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/model/associations.rb +9 -9
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +1 -1
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +64 -8
- data/spec/adapters/postgres_spec.rb +139 -78
- data/spec/adapters/sqlite_spec.rb +87 -0
- data/spec/core/connection_pool_spec.rb +14 -0
- data/spec/core/database_spec.rb +5 -0
- data/spec/core/mock_adapter_spec.rb +21 -9
- data/spec/extensions/arbitrary_servers_spec.rb +114 -0
- data/spec/extensions/instance_hooks_spec.rb +19 -0
- data/spec/extensions/list_spec.rb +31 -0
- data/spec/extensions/migration_spec.rb +61 -4
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/files/transaction_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_migrations/002_create_basic.rb +3 -0
- data/spec/files/transactionless_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transactionless_migrations/002_create_basic.rb +4 -0
- data/spec/integration/dataset_test.rb +2 -2
- data/spec/integration/plugin_test.rb +9 -9
- data/spec/integration/schema_test.rb +3 -1
- data/spec/integration/transaction_test.rb +2 -2
- 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
|
-
|
16
|
-
def
|
17
|
-
|
18
|
-
|
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' =>
|
25
|
-
%w'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' =>
|
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
|
-
|
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
|
361
|
+
if type_proc && v
|
307
362
|
v = type_proc.call(v)
|
308
363
|
end
|
309
364
|
row[name] = v
|
@@ -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 =
|
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
|
-
|
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
|
34
|
-
# be
|
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
|
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
|
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
|
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
|
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
|
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
|
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)
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -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 << "
|
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
|
-
#
|
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
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
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
|
-
|
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
|