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.
Files changed (51) hide show
  1. checksums.yaml +5 -13
  2. data/.rubocop.yml +256 -0
  3. data/.travis.yml +2 -3
  4. data/CHANGELOG.md +21 -0
  5. data/README.md +30 -3
  6. data/Rakefile +2 -2
  7. data/bin/lhm-config.sh +7 -0
  8. data/bin/lhm-kill-queue +13 -15
  9. data/bin/lhm-spec-clobber.sh +1 -1
  10. data/bin/lhm-spec-grants.sh +2 -2
  11. data/bin/lhm-spec-setup-cluster.sh +1 -1
  12. data/gemfiles/ar-2.3_mysql.gemfile +1 -0
  13. data/lhm.gemspec +6 -6
  14. data/lib/lhm.rb +20 -8
  15. data/lib/lhm/atomic_switcher.rb +2 -1
  16. data/lib/lhm/chunker.rb +19 -19
  17. data/lib/lhm/command.rb +1 -1
  18. data/lib/lhm/connection.rb +12 -0
  19. data/lib/lhm/entangler.rb +5 -5
  20. data/lib/lhm/intersection.rb +29 -16
  21. data/lib/lhm/invoker.rb +3 -3
  22. data/lib/lhm/locked_switcher.rb +6 -6
  23. data/lib/lhm/migration.rb +5 -4
  24. data/lib/lhm/migrator.rb +39 -10
  25. data/lib/lhm/printer.rb +4 -6
  26. data/lib/lhm/sql_helper.rb +4 -4
  27. data/lib/lhm/table.rb +12 -12
  28. data/lib/lhm/version.rb +1 -1
  29. data/spec/fixtures/users.ddl +1 -1
  30. data/spec/integration/atomic_switcher_spec.rb +7 -7
  31. data/spec/integration/chunker_spec.rb +2 -2
  32. data/spec/integration/cleanup_spec.rb +34 -10
  33. data/spec/integration/entangler_spec.rb +11 -11
  34. data/spec/integration/integration_helper.rb +10 -3
  35. data/spec/integration/lhm_spec.rb +96 -46
  36. data/spec/integration/locked_switcher_spec.rb +7 -7
  37. data/spec/integration/table_spec.rb +15 -15
  38. data/spec/test_helper.rb +4 -4
  39. data/spec/unit/atomic_switcher_spec.rb +6 -6
  40. data/spec/unit/chunker_spec.rb +22 -9
  41. data/spec/unit/entangler_spec.rb +19 -19
  42. data/spec/unit/intersection_spec.rb +27 -15
  43. data/spec/unit/lhm_spec.rb +6 -6
  44. data/spec/unit/locked_switcher_spec.rb +14 -14
  45. data/spec/unit/migration_spec.rb +6 -6
  46. data/spec/unit/migrator_spec.rb +53 -41
  47. data/spec/unit/printer_spec.rb +7 -7
  48. data/spec/unit/sql_helper_spec.rb +10 -10
  49. data/spec/unit/table_spec.rb +11 -11
  50. data/spec/unit/throttler_spec.rb +12 -12
  51. metadata +12 -10
@@ -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("100% complete")
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}" + (" " * extra)
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
@@ -6,12 +6,12 @@ module Lhm
6
6
  extend self
7
7
 
8
8
  def annotation
9
- "/* large hadron migration */"
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("_and_") }"
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, "Value")
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
@@ -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 = "id", ddl = nil)
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 == "id"
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, "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")
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, "Key_name")
87
- column_name = struct_key(row, "COLUMN_NAME")
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, "COLUMN_KEY")
99
- defn[column_key] == "PRI"
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, "COLUMN_NAME")
103
+ column_name = struct_key(defn, 'COLUMN_NAME')
104
104
  defn[column_name]
105
105
  end
106
106
 
@@ -2,5 +2,5 @@
2
2
  # Schmidt
3
3
 
4
4
  module Lhm
5
- VERSION = "2.1.0"
5
+ VERSION = '2.2.0'
6
6
  end
@@ -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 NULL,
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 "switching" do
15
+ describe 'switching' do
16
16
  before(:each) do
17
- @origin = table_create("origin")
18
- @destination = table_create("destination")
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 "rename origin to archive" do
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 "origin"
28
+ table_read(@migration.archive_name).columns.keys.must_include 'origin'
29
29
  end
30
30
  end
31
31
 
32
- it "rename destination to origin" do
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 "destination"
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 "copying" do
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 "should copy 23 rows from origin to destination" do
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, "cleanup" do
6
+ describe Lhm, 'cleanup' do
7
7
  include IntegrationHelper
8
8
  before(:each) { connect_master! }
9
9
 
10
- describe "changes" do
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 "should show temporary tables" do
25
+ it 'should show temporary tables' do
26
26
  output = capture_stdout do
27
27
  Lhm.cleanup
28
28
  end
29
- output.must_include("Existing LHM backup tables")
29
+ output.must_include('Existing LHM backup tables')
30
30
  output.must_match(/lhma_[0-9_]*_users/)
31
31
  end
32
32
 
33
- it "should show temporary triggers" do
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("Existing LHM triggers")
38
- output.must_include("lhmt_ins_users")
39
- output.must_include("lhmt_del")
40
- output.must_include("lhmt_upd_users")
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 "should delete temporary tables" do
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 "entanglement" do
15
+ describe 'entanglement' do
16
16
  before(:each) do
17
- @origin = table_create("origin")
18
- @destination = table_create("destination")
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 "should replay inserts from origin into destination" do
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, "common", "inserted").must_equal(1)
29
+ count(:destination, 'common', 'inserted').must_equal(1)
30
30
  end
31
31
  end
32
32
 
33
- it "should replay deletes from origin into destination" do
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, "common", "inserted").must_equal(0)
41
+ count(:destination, 'common', 'inserted').must_equal(0)
42
42
  end
43
43
  end
44
44
 
45
- it "should replay updates from origin into destination" do
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, "common", "updated").must_equal(1)
52
+ count(:destination, 'common', 'updated').must_equal(1)
53
53
  end
54
54
  end
55
55
 
56
- it "should remove entanglement" do
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, "common", "inserted").must_equal(0)
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
- $password = YAML.load_file(File.expand_path(File.dirname(__FILE__)) + "/database.yml")["password"] rescue nil
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["MASTER_SLAVE"]
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 "changes" do
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 "when providing a subset of data to copy" do
18
+ describe 'when providing a subset of data to copy' do
19
19
 
20
20
  before do
21
- execute("insert into tracks set id = 13, public = 0")
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("inner join tracks on tracks.`id` = permissions.`track_id` and tracks.`public` = 1")
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 "when no additional data is inserted into the table" do
30
+ describe 'when no additional data is inserted into the table' do
31
31
 
32
- it "migrates the existing data" do
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 "when additional data is inserted" do
39
+ describe 'when additional data is inserted' do
40
40
 
41
41
  before do
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")
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 "migrates all data" do
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 "should add a column" do
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["logins"].must_equal({
63
- :type => "int(12)",
64
- :is_nullable => "YES",
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 "should copy all rows" do
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 "should remove a column" do
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["comment"].must_equal nil
88
+ table_read(:users).columns['comment'].must_equal nil
89
89
  end
90
90
  end
91
91
 
92
- it "should add an index" do
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 "should add an index with a custom name" do
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 "should add an index on a column with a reserved name" do
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 "should add a unqiue index" do
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 "should remove an index" do
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 "should remove an index with a custom name" do
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 "should remove an index with a custom name by name" do
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 "should apply a ddl statement" do
162
+ it 'should apply a ddl statement' do
163
163
  Lhm.change_table(:users, :atomic_switch => false) do |t|
164
- t.ddl("alter table %s add column flag tinyint(1)" % t.name)
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["flag"].must_equal({
169
- :type => "tinyint(1)",
170
- :is_nullable => "YES",
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 "should change a column" do
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["comment"].must_equal({
183
- :type => "varchar(20)",
184
- :is_nullable => "NO",
185
- :column_default => "none"
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 "should change the last column in a table" do
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, "int(5)")
194
+ t.change_column(:id, 'int(5)')
195
195
  end
196
196
 
197
197
  slave do
198
- table_read(:small_table).columns["id"].must_equal({
199
- :type => "int(5)",
200
- :is_nullable => "NO",
201
- :column_default => "0"
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 "works when mysql reserved words are used" do
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 "parallel" do
229
- it "should perserve inserts during migration" do
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 "should perserve deletes during migration" do
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