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.
- 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
|