lhm 2.1.0 → 2.2.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.
- checksums.yaml +5 -13
- data/.rubocop.yml +256 -0
- data/.travis.yml +2 -3
- data/CHANGELOG.md +21 -0
- data/README.md +30 -3
- data/Rakefile +2 -2
- data/bin/lhm-config.sh +7 -0
- data/bin/lhm-kill-queue +13 -15
- data/bin/lhm-spec-clobber.sh +1 -1
- data/bin/lhm-spec-grants.sh +2 -2
- data/bin/lhm-spec-setup-cluster.sh +1 -1
- data/gemfiles/ar-2.3_mysql.gemfile +1 -0
- data/lhm.gemspec +6 -6
- data/lib/lhm.rb +20 -8
- data/lib/lhm/atomic_switcher.rb +2 -1
- data/lib/lhm/chunker.rb +19 -19
- data/lib/lhm/command.rb +1 -1
- data/lib/lhm/connection.rb +12 -0
- data/lib/lhm/entangler.rb +5 -5
- data/lib/lhm/intersection.rb +29 -16
- data/lib/lhm/invoker.rb +3 -3
- data/lib/lhm/locked_switcher.rb +6 -6
- data/lib/lhm/migration.rb +5 -4
- data/lib/lhm/migrator.rb +39 -10
- data/lib/lhm/printer.rb +4 -6
- data/lib/lhm/sql_helper.rb +4 -4
- data/lib/lhm/table.rb +12 -12
- data/lib/lhm/version.rb +1 -1
- data/spec/fixtures/users.ddl +1 -1
- data/spec/integration/atomic_switcher_spec.rb +7 -7
- data/spec/integration/chunker_spec.rb +2 -2
- data/spec/integration/cleanup_spec.rb +34 -10
- data/spec/integration/entangler_spec.rb +11 -11
- data/spec/integration/integration_helper.rb +10 -3
- data/spec/integration/lhm_spec.rb +96 -46
- data/spec/integration/locked_switcher_spec.rb +7 -7
- data/spec/integration/table_spec.rb +15 -15
- data/spec/test_helper.rb +4 -4
- data/spec/unit/atomic_switcher_spec.rb +6 -6
- data/spec/unit/chunker_spec.rb +22 -9
- data/spec/unit/entangler_spec.rb +19 -19
- data/spec/unit/intersection_spec.rb +27 -15
- data/spec/unit/lhm_spec.rb +6 -6
- data/spec/unit/locked_switcher_spec.rb +14 -14
- data/spec/unit/migration_spec.rb +6 -6
- data/spec/unit/migrator_spec.rb +53 -41
- data/spec/unit/printer_spec.rb +7 -7
- data/spec/unit/sql_helper_spec.rb +10 -10
- data/spec/unit/table_spec.rb +11 -11
- data/spec/unit/throttler_spec.rb +12 -12
- metadata +12 -10
data/lib/lhm/printer.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Lhm
|
2
2
|
module Printer
|
3
|
-
|
4
3
|
class Output
|
5
4
|
def write(message)
|
6
5
|
print message
|
@@ -8,14 +7,12 @@ module Lhm
|
|
8
7
|
end
|
9
8
|
|
10
9
|
class Base
|
11
|
-
|
12
10
|
def initialize
|
13
11
|
@output = Output.new
|
14
12
|
end
|
15
13
|
end
|
16
14
|
|
17
15
|
class Percentage < Base
|
18
|
-
|
19
16
|
def initialize
|
20
17
|
super
|
21
18
|
@max_length = 0
|
@@ -28,24 +25,25 @@ module Lhm
|
|
28
25
|
end
|
29
26
|
|
30
27
|
def end
|
31
|
-
write(
|
28
|
+
write('100% complete')
|
32
29
|
@output.write "\n"
|
33
30
|
end
|
34
31
|
|
35
32
|
private
|
33
|
+
|
36
34
|
def write(message)
|
37
35
|
if (extra = @max_length - message.length) < 0
|
38
36
|
@max_length = message.length
|
39
37
|
extra = 0
|
40
38
|
end
|
41
39
|
|
42
|
-
@output.write "\r#{message}" + (
|
40
|
+
@output.write "\r#{message}" + (' ' * extra)
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
44
|
class Dot < Base
|
47
45
|
def notify(lowest = nil, highest = nil)
|
48
|
-
@output.write
|
46
|
+
@output.write '.'
|
49
47
|
end
|
50
48
|
|
51
49
|
def end
|
data/lib/lhm/sql_helper.rb
CHANGED
@@ -6,12 +6,12 @@ module Lhm
|
|
6
6
|
extend self
|
7
7
|
|
8
8
|
def annotation
|
9
|
-
|
9
|
+
'/* large hadron migration */'
|
10
10
|
end
|
11
11
|
|
12
12
|
def idx_name(table_name, cols)
|
13
13
|
column_names = column_definition(cols).map(&:first)
|
14
|
-
"index_#{ table_name }_on_#{ column_names.join(
|
14
|
+
"index_#{ table_name }_on_#{ column_names.join('_and_') }"
|
15
15
|
end
|
16
16
|
|
17
17
|
def idx_spec(cols)
|
@@ -22,7 +22,7 @@ module Lhm
|
|
22
22
|
|
23
23
|
def version_string
|
24
24
|
row = connection.select_one("show variables like 'version'")
|
25
|
-
value = struct_key(row,
|
25
|
+
value = struct_key(row, 'Value')
|
26
26
|
row[value]
|
27
27
|
end
|
28
28
|
|
@@ -71,7 +71,7 @@ module Lhm
|
|
71
71
|
struct.members
|
72
72
|
end
|
73
73
|
|
74
|
-
keys.find {|k| k.to_s.downcase == key.to_s.downcase }
|
74
|
+
keys.find { |k| k.to_s.downcase == key.to_s.downcase }
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
data/lib/lhm/table.rb
CHANGED
@@ -7,7 +7,7 @@ module Lhm
|
|
7
7
|
class Table
|
8
8
|
attr_reader :name, :columns, :indices, :pk, :ddl
|
9
9
|
|
10
|
-
def initialize(name, pk =
|
10
|
+
def initialize(name, pk = 'id', ddl = nil)
|
11
11
|
@name = name
|
12
12
|
@columns = {}
|
13
13
|
@indices = {}
|
@@ -16,7 +16,7 @@ module Lhm
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def satisfies_primary_key?
|
19
|
-
@pk ==
|
19
|
+
@pk == 'id'
|
20
20
|
end
|
21
21
|
|
22
22
|
def destination_name
|
@@ -45,10 +45,10 @@ module Lhm
|
|
45
45
|
|
46
46
|
Table.new(@table_name, extract_primary_key(schema), ddl).tap do |table|
|
47
47
|
schema.each do |defn|
|
48
|
-
column_name = struct_key(defn,
|
49
|
-
column_type = struct_key(defn,
|
50
|
-
is_nullable = struct_key(defn,
|
51
|
-
column_default = struct_key(defn,
|
48
|
+
column_name = struct_key(defn, 'COLUMN_NAME')
|
49
|
+
column_type = struct_key(defn, 'COLUMN_TYPE')
|
50
|
+
is_nullable = struct_key(defn, 'IS_NULLABLE')
|
51
|
+
column_default = struct_key(defn, 'COLUMN_DEFAULT')
|
52
52
|
table.columns[defn[column_name]] = {
|
53
53
|
:type => defn[column_type],
|
54
54
|
:is_nullable => defn[is_nullable],
|
@@ -83,11 +83,11 @@ module Lhm
|
|
83
83
|
def extract_indices(indices)
|
84
84
|
indices.
|
85
85
|
map do |row|
|
86
|
-
key_name = struct_key(row,
|
87
|
-
column_name = struct_key(row,
|
86
|
+
key_name = struct_key(row, 'Key_name')
|
87
|
+
column_name = struct_key(row, 'COLUMN_NAME')
|
88
88
|
[row[key_name], row[column_name]]
|
89
89
|
end.
|
90
|
-
inject(Hash.new { |h, k| h[k] = []}) do |memo, (idx, column)|
|
90
|
+
inject(Hash.new { |h, k| h[k] = [] }) do |memo, (idx, column)|
|
91
91
|
memo[idx] << column
|
92
92
|
memo
|
93
93
|
end
|
@@ -95,12 +95,12 @@ module Lhm
|
|
95
95
|
|
96
96
|
def extract_primary_key(schema)
|
97
97
|
cols = schema.select do |defn|
|
98
|
-
column_key = struct_key(defn,
|
99
|
-
defn[column_key] ==
|
98
|
+
column_key = struct_key(defn, 'COLUMN_KEY')
|
99
|
+
defn[column_key] == 'PRI'
|
100
100
|
end
|
101
101
|
|
102
102
|
keys = cols.map do |defn|
|
103
|
-
column_name = struct_key(defn,
|
103
|
+
column_name = struct_key(defn, 'COLUMN_NAME')
|
104
104
|
defn[column_name]
|
105
105
|
end
|
106
106
|
|
data/lib/lhm/version.rb
CHANGED
data/spec/fixtures/users.ddl
CHANGED
@@ -2,7 +2,7 @@ CREATE TABLE `users` (
|
|
2
2
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
3
3
|
`reference` int(11) DEFAULT NULL,
|
4
4
|
`username` varchar(255) DEFAULT NULL,
|
5
|
-
`group` varchar(255) DEFAULT
|
5
|
+
`group` varchar(255) DEFAULT 'Superfriends',
|
6
6
|
`created_at` datetime DEFAULT NULL,
|
7
7
|
`comment` varchar(20) DEFAULT NULL,
|
8
8
|
`description` text,
|
@@ -12,30 +12,30 @@ describe Lhm::AtomicSwitcher do
|
|
12
12
|
|
13
13
|
before(:each) { connect_master! }
|
14
14
|
|
15
|
-
describe
|
15
|
+
describe 'switching' do
|
16
16
|
before(:each) do
|
17
|
-
@origin = table_create(
|
18
|
-
@destination = table_create(
|
17
|
+
@origin = table_create('origin')
|
18
|
+
@destination = table_create('destination')
|
19
19
|
@migration = Lhm::Migration.new(@origin, @destination)
|
20
20
|
end
|
21
21
|
|
22
|
-
it
|
22
|
+
it 'rename origin to archive' do
|
23
23
|
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
24
24
|
switcher.run
|
25
25
|
|
26
26
|
slave do
|
27
27
|
table_exists?(@origin).must_equal true
|
28
|
-
table_read(@migration.archive_name).columns.keys.must_include
|
28
|
+
table_read(@migration.archive_name).columns.keys.must_include 'origin'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
it
|
32
|
+
it 'rename destination to origin' do
|
33
33
|
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
34
34
|
switcher.run
|
35
35
|
|
36
36
|
slave do
|
37
37
|
table_exists?(@destination).must_equal false
|
38
|
-
table_read(@origin.name).columns.keys.must_include
|
38
|
+
table_read(@origin.name).columns.keys.must_include 'destination'
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -10,14 +10,14 @@ describe Lhm::Chunker do
|
|
10
10
|
|
11
11
|
before(:each) { connect_master! }
|
12
12
|
|
13
|
-
describe
|
13
|
+
describe 'copying' do
|
14
14
|
before(:each) do
|
15
15
|
@origin = table_create(:origin)
|
16
16
|
@destination = table_create(:destination)
|
17
17
|
@migration = Lhm::Migration.new(@origin, @destination)
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
20
|
+
it 'should copy 23 rows from origin to destination' do
|
21
21
|
23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
|
22
22
|
|
23
23
|
printer = MiniTest::Mock.new
|
@@ -3,11 +3,11 @@
|
|
3
3
|
|
4
4
|
require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
|
5
5
|
|
6
|
-
describe Lhm,
|
6
|
+
describe Lhm, 'cleanup' do
|
7
7
|
include IntegrationHelper
|
8
8
|
before(:each) { connect_master! }
|
9
9
|
|
10
|
-
describe
|
10
|
+
describe 'changes' do
|
11
11
|
before(:each) do
|
12
12
|
table_create(:users)
|
13
13
|
simulate_failed_migration do
|
@@ -22,25 +22,49 @@ describe Lhm, "cleanup" do
|
|
22
22
|
Lhm.cleanup(true)
|
23
23
|
end
|
24
24
|
|
25
|
-
it
|
25
|
+
it 'should show temporary tables' do
|
26
26
|
output = capture_stdout do
|
27
27
|
Lhm.cleanup
|
28
28
|
end
|
29
|
-
output.must_include(
|
29
|
+
output.must_include('Existing LHM backup tables')
|
30
30
|
output.must_match(/lhma_[0-9_]*_users/)
|
31
31
|
end
|
32
32
|
|
33
|
-
it
|
33
|
+
it 'should show temporary tables within range' do
|
34
|
+
table = OpenStruct.new(:name => 'users')
|
35
|
+
table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now - 172800).archive_name
|
36
|
+
table_rename(:users, table_name)
|
37
|
+
|
38
|
+
output = capture_stdout do
|
39
|
+
Lhm.cleanup false, { :until => Time.now - 86400 }
|
40
|
+
end
|
41
|
+
output.must_include('Existing LHM backup tables')
|
42
|
+
output.must_match(/lhma_[0-9_]*_users/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should exclude temporary tables outside range' do
|
46
|
+
table = OpenStruct.new(:name => 'users')
|
47
|
+
table_name = Lhm::Migration.new(table, nil, nil, {}, Time.now).archive_name
|
48
|
+
table_rename(:users, table_name)
|
49
|
+
|
50
|
+
output = capture_stdout do
|
51
|
+
Lhm.cleanup false, { :until => Time.now - 172800 }
|
52
|
+
end
|
53
|
+
output.must_include('Existing LHM backup tables')
|
54
|
+
output.wont_match(/lhma_[0-9_]*_users/)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should show temporary triggers' do
|
34
58
|
output = capture_stdout do
|
35
59
|
Lhm.cleanup
|
36
60
|
end
|
37
|
-
output.must_include(
|
38
|
-
output.must_include(
|
39
|
-
output.must_include(
|
40
|
-
output.must_include(
|
61
|
+
output.must_include('Existing LHM triggers')
|
62
|
+
output.must_include('lhmt_ins_users')
|
63
|
+
output.must_include('lhmt_del')
|
64
|
+
output.must_include('lhmt_upd_users')
|
41
65
|
end
|
42
66
|
|
43
|
-
it
|
67
|
+
it 'should delete temporary tables' do
|
44
68
|
Lhm.cleanup(true).must_equal(true)
|
45
69
|
Lhm.cleanup.must_equal(true)
|
46
70
|
end
|
@@ -12,25 +12,25 @@ describe Lhm::Entangler do
|
|
12
12
|
|
13
13
|
before(:each) { connect_master! }
|
14
14
|
|
15
|
-
describe
|
15
|
+
describe 'entanglement' do
|
16
16
|
before(:each) do
|
17
|
-
@origin = table_create(
|
18
|
-
@destination = table_create(
|
17
|
+
@origin = table_create('origin')
|
18
|
+
@destination = table_create('destination')
|
19
19
|
@migration = Lhm::Migration.new(@origin, @destination)
|
20
20
|
@entangler = Lhm::Entangler.new(@migration, connection)
|
21
21
|
end
|
22
22
|
|
23
|
-
it
|
23
|
+
it 'should replay inserts from origin into destination' do
|
24
24
|
@entangler.run do |entangler|
|
25
25
|
execute("insert into origin (common) values ('inserted')")
|
26
26
|
end
|
27
27
|
|
28
28
|
slave do
|
29
|
-
count(:destination,
|
29
|
+
count(:destination, 'common', 'inserted').must_equal(1)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
it
|
33
|
+
it 'should replay deletes from origin into destination' do
|
34
34
|
execute("insert into origin (common) values ('inserted')")
|
35
35
|
|
36
36
|
@entangler.run do |entangler|
|
@@ -38,28 +38,28 @@ describe Lhm::Entangler do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
slave do
|
41
|
-
count(:destination,
|
41
|
+
count(:destination, 'common', 'inserted').must_equal(0)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
it
|
45
|
+
it 'should replay updates from origin into destination' do
|
46
46
|
@entangler.run do |entangler|
|
47
47
|
execute("insert into origin (common) values ('inserted')")
|
48
48
|
execute("update origin set common = 'updated'")
|
49
49
|
end
|
50
50
|
|
51
51
|
slave do
|
52
|
-
count(:destination,
|
52
|
+
count(:destination, 'common', 'updated').must_equal(1)
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
it
|
56
|
+
it 'should remove entanglement' do
|
57
57
|
@entangler.run {}
|
58
58
|
|
59
59
|
execute("insert into origin (common) values ('inserted')")
|
60
60
|
|
61
61
|
slave do
|
62
|
-
count(:destination,
|
62
|
+
count(:destination, 'common', 'inserted').must_equal(0)
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
@@ -2,7 +2,11 @@
|
|
2
2
|
# Schmidt
|
3
3
|
require 'test_helper'
|
4
4
|
require 'yaml'
|
5
|
-
|
5
|
+
begin
|
6
|
+
require 'active_support'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
$password = YAML.load_file(File.expand_path(File.dirname(__FILE__)) + '/database.yml')['password'] rescue nil
|
6
10
|
|
7
11
|
require 'lhm/table'
|
8
12
|
require 'lhm/sql_helper'
|
@@ -82,7 +86,6 @@ module IntegrationHelper
|
|
82
86
|
connect_master!
|
83
87
|
end
|
84
88
|
|
85
|
-
|
86
89
|
yield block
|
87
90
|
|
88
91
|
if master_slave_mode?
|
@@ -104,6 +107,10 @@ module IntegrationHelper
|
|
104
107
|
table_read(fixture_name)
|
105
108
|
end
|
106
109
|
|
110
|
+
def table_rename(from_name, to_name)
|
111
|
+
execute "rename table `#{ from_name }` to `#{ to_name }`"
|
112
|
+
end
|
113
|
+
|
107
114
|
def table_read(fixture_name)
|
108
115
|
Lhm::Table.parse(fixture_name, @connection)
|
109
116
|
end
|
@@ -147,7 +154,7 @@ module IntegrationHelper
|
|
147
154
|
#
|
148
155
|
|
149
156
|
def master_slave_mode?
|
150
|
-
!!ENV[
|
157
|
+
!!ENV['MASTER_SLAVE']
|
151
158
|
end
|
152
159
|
|
153
160
|
#
|
@@ -8,44 +8,44 @@ describe Lhm do
|
|
8
8
|
|
9
9
|
before(:each) { connect_master! }
|
10
10
|
|
11
|
-
describe
|
11
|
+
describe 'changes' do
|
12
12
|
before(:each) do
|
13
13
|
table_create(:users)
|
14
14
|
table_create(:tracks)
|
15
15
|
table_create(:permissions)
|
16
16
|
end
|
17
17
|
|
18
|
-
describe
|
18
|
+
describe 'when providing a subset of data to copy' do
|
19
19
|
|
20
20
|
before do
|
21
|
-
execute(
|
21
|
+
execute('insert into tracks set id = 13, public = 0')
|
22
22
|
11.times { |n| execute("insert into tracks set id = #{n + 1}, public = 1") }
|
23
23
|
11.times { |n| execute("insert into permissions set track_id = #{n + 1}") }
|
24
24
|
|
25
25
|
Lhm.change_table(:permissions, :atomic_switch => false) do |t|
|
26
|
-
t.filter(
|
26
|
+
t.filter('inner join tracks on tracks.`id` = permissions.`track_id` and tracks.`public` = 1')
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
describe
|
30
|
+
describe 'when no additional data is inserted into the table' do
|
31
31
|
|
32
|
-
it
|
32
|
+
it 'migrates the existing data' do
|
33
33
|
slave do
|
34
34
|
count_all(:permissions).must_equal(11)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
describe
|
39
|
+
describe 'when additional data is inserted' do
|
40
40
|
|
41
41
|
before do
|
42
|
-
execute(
|
43
|
-
execute(
|
44
|
-
execute(
|
45
|
-
execute(
|
42
|
+
execute('insert into tracks set id = 14, public = 0')
|
43
|
+
execute('insert into tracks set id = 15, public = 1')
|
44
|
+
execute('insert into permissions set track_id = 14')
|
45
|
+
execute('insert into permissions set track_id = 15')
|
46
46
|
end
|
47
47
|
|
48
|
-
it
|
48
|
+
it 'migrates all data' do
|
49
49
|
slave do
|
50
50
|
count_all(:permissions).must_equal(13)
|
51
51
|
end
|
@@ -53,21 +53,21 @@ describe Lhm do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
it
|
56
|
+
it 'should add a column' do
|
57
57
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
58
58
|
t.add_column(:logins, "INT(12) DEFAULT '0'")
|
59
59
|
end
|
60
60
|
|
61
61
|
slave do
|
62
|
-
table_read(:users).columns[
|
63
|
-
:type =>
|
64
|
-
:is_nullable =>
|
62
|
+
table_read(:users).columns['logins'].must_equal({
|
63
|
+
:type => 'int(12)',
|
64
|
+
:is_nullable => 'YES',
|
65
65
|
:column_default => '0'
|
66
66
|
})
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
it
|
70
|
+
it 'should copy all rows' do
|
71
71
|
23.times { |n| execute("insert into users set reference = '#{ n }'") }
|
72
72
|
|
73
73
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
@@ -79,17 +79,17 @@ describe Lhm do
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
it
|
82
|
+
it 'should remove a column' do
|
83
83
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
84
84
|
t.remove_column(:comment)
|
85
85
|
end
|
86
86
|
|
87
87
|
slave do
|
88
|
-
table_read(:users).columns[
|
88
|
+
table_read(:users).columns['comment'].must_equal nil
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
it
|
92
|
+
it 'should add an index' do
|
93
93
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
94
94
|
t.add_index([:comment, :created_at])
|
95
95
|
end
|
@@ -99,7 +99,7 @@ describe Lhm do
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
it
|
102
|
+
it 'should add an index with a custom name' do
|
103
103
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
104
104
|
t.add_index([:comment, :created_at], :my_index_name)
|
105
105
|
end
|
@@ -109,7 +109,7 @@ describe Lhm do
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
it
|
112
|
+
it 'should add an index on a column with a reserved name' do
|
113
113
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
114
114
|
t.add_index(:group)
|
115
115
|
end
|
@@ -119,7 +119,7 @@ describe Lhm do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
-
it
|
122
|
+
it 'should add a unqiue index' do
|
123
123
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
124
124
|
t.add_unique_index(:comment)
|
125
125
|
end
|
@@ -129,7 +129,7 @@ describe Lhm do
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
-
it
|
132
|
+
it 'should remove an index' do
|
133
133
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
134
134
|
t.remove_index([:username, :created_at])
|
135
135
|
end
|
@@ -139,7 +139,7 @@ describe Lhm do
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
-
it
|
142
|
+
it 'should remove an index with a custom name' do
|
143
143
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
144
144
|
t.remove_index([:username, :group])
|
145
145
|
end
|
@@ -149,7 +149,7 @@ describe Lhm do
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
-
it
|
152
|
+
it 'should remove an index with a custom name by name' do
|
153
153
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
154
154
|
t.remove_index(:irrelevant_column_name, :index_with_a_custom_name)
|
155
155
|
end
|
@@ -159,51 +159,101 @@ describe Lhm do
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
-
it
|
162
|
+
it 'should apply a ddl statement' do
|
163
163
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
164
|
-
t.ddl(
|
164
|
+
t.ddl('alter table %s add column flag tinyint(1)' % t.name)
|
165
165
|
end
|
166
166
|
|
167
167
|
slave do
|
168
|
-
table_read(:users).columns[
|
169
|
-
:type =>
|
170
|
-
:is_nullable =>
|
168
|
+
table_read(:users).columns['flag'].must_equal({
|
169
|
+
:type => 'tinyint(1)',
|
170
|
+
:is_nullable => 'YES',
|
171
171
|
:column_default => nil
|
172
172
|
})
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
-
it
|
176
|
+
it 'should change a column' do
|
177
177
|
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
178
178
|
t.change_column(:comment, "varchar(20) DEFAULT 'none' NOT NULL")
|
179
179
|
end
|
180
180
|
|
181
181
|
slave do
|
182
|
-
table_read(:users).columns[
|
183
|
-
:type =>
|
184
|
-
:is_nullable =>
|
185
|
-
:column_default =>
|
182
|
+
table_read(:users).columns['comment'].must_equal({
|
183
|
+
:type => 'varchar(20)',
|
184
|
+
:is_nullable => 'NO',
|
185
|
+
:column_default => 'none'
|
186
186
|
})
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
190
|
-
it
|
190
|
+
it 'should change the last column in a table' do
|
191
191
|
table_create(:small_table)
|
192
192
|
|
193
193
|
Lhm.change_table(:small_table, :atomic_switch => false) do |t|
|
194
|
-
t.change_column(:id,
|
194
|
+
t.change_column(:id, 'int(5)')
|
195
195
|
end
|
196
196
|
|
197
197
|
slave do
|
198
|
-
table_read(:small_table).columns[
|
199
|
-
:type =>
|
200
|
-
:is_nullable =>
|
201
|
-
:column_default =>
|
198
|
+
table_read(:small_table).columns['id'].must_equal({
|
199
|
+
:type => 'int(5)',
|
200
|
+
:is_nullable => 'NO',
|
201
|
+
:column_default => '0'
|
202
|
+
})
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should rename a column' do
|
207
|
+
table_create(:users)
|
208
|
+
|
209
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
210
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
211
|
+
t.rename_column(:username, :login)
|
212
|
+
end
|
213
|
+
|
214
|
+
slave do
|
215
|
+
table_data = table_read(:users)
|
216
|
+
table_data.columns['username'].must_equal(nil)
|
217
|
+
table_read(:users).columns['login'].must_equal({
|
218
|
+
:type => 'varchar(255)',
|
219
|
+
:is_nullable => 'YES',
|
220
|
+
:column_default => nil
|
202
221
|
})
|
222
|
+
|
223
|
+
# DM & AR versions of select_one return different structures. The
|
224
|
+
# real test is whether the data was copied
|
225
|
+
result = select_one('SELECT login from users')
|
226
|
+
result = result['login'] if result.respond_to?(:has_key?)
|
227
|
+
result.must_equal('a user')
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'should rename a column with a default' do
|
232
|
+
table_create(:users)
|
233
|
+
|
234
|
+
execute("INSERT INTO users (username) VALUES ('a user')")
|
235
|
+
Lhm.change_table(:users, :atomic_switch => false) do |t|
|
236
|
+
t.rename_column(:group, :fnord)
|
237
|
+
end
|
238
|
+
|
239
|
+
slave do
|
240
|
+
table_data = table_read(:users)
|
241
|
+
table_data.columns['group'].must_equal(nil)
|
242
|
+
table_read(:users).columns['fnord'].must_equal({
|
243
|
+
:type => 'varchar(255)',
|
244
|
+
:is_nullable => 'YES',
|
245
|
+
:column_default => 'Superfriends'
|
246
|
+
})
|
247
|
+
|
248
|
+
# DM & AR versions of select_one return different structures. The
|
249
|
+
# real test is whether the data was copied
|
250
|
+
result = select_one('SELECT `fnord` from users')
|
251
|
+
result = result['fnord'] if result.respond_to?(:has_key?)
|
252
|
+
result.must_equal('Superfriends')
|
203
253
|
end
|
204
254
|
end
|
205
255
|
|
206
|
-
it
|
256
|
+
it 'works when mysql reserved words are used' do
|
207
257
|
table_create(:lines)
|
208
258
|
execute("insert into `lines` set id = 1, `between` = 'foo'")
|
209
259
|
execute("insert into `lines` set id = 2, `between` = 'bar'")
|
@@ -225,8 +275,8 @@ describe Lhm do
|
|
225
275
|
end
|
226
276
|
end
|
227
277
|
|
228
|
-
describe
|
229
|
-
it
|
278
|
+
describe 'parallel' do
|
279
|
+
it 'should perserve inserts during migration' do
|
230
280
|
50.times { |n| execute("insert into users set reference = '#{ n }'") }
|
231
281
|
|
232
282
|
insert = Thread.new do
|
@@ -250,7 +300,7 @@ describe Lhm do
|
|
250
300
|
end
|
251
301
|
end
|
252
302
|
|
253
|
-
it
|
303
|
+
it 'should perserve deletes during migration' do
|
254
304
|
50.times { |n| execute("insert into users set reference = '#{ n }'") }
|
255
305
|
|
256
306
|
delete = Thread.new do
|