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/bin/lhm-spec-clobber.sh
CHANGED
data/bin/lhm-spec-grants.sh
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/sh
|
2
2
|
|
3
|
-
source
|
3
|
+
source `dirname $0`/lhm-config.sh
|
4
4
|
|
5
5
|
master() { "$mysqldir"/bin/mysql --protocol=TCP -P $master_port -uroot; }
|
6
6
|
slave() { "$mysqldir"/bin/mysql --protocol=TCP -P $slave_port -uroot; }
|
@@ -12,7 +12,7 @@ echo "grant replication slave on *.* to 'slave'@'localhost'" | master
|
|
12
12
|
|
13
13
|
# set up slave
|
14
14
|
|
15
|
-
echo "change master to master_user = 'slave', master_password = 'slave', master_port =
|
15
|
+
echo "change master to master_user = 'slave', master_password = 'slave', master_port = $master_port, master_host = 'localhost'" | slave
|
16
16
|
echo "start slave" | slave
|
17
17
|
echo "show slave status \G" | slave
|
18
18
|
|
data/lhm.gemspec
CHANGED
@@ -6,19 +6,19 @@ $:.unshift(lib) unless $:.include?(lib)
|
|
6
6
|
require 'lhm/version'
|
7
7
|
|
8
8
|
Gem::Specification.new do |s|
|
9
|
-
s.name =
|
9
|
+
s.name = 'lhm'
|
10
10
|
s.version = Lhm::VERSION
|
11
11
|
s.platform = Gem::Platform::RUBY
|
12
|
-
s.authors = [
|
12
|
+
s.authors = ['SoundCloud', 'Rany Keddo', 'Tobias Bielohlawek', 'Tobias Schmidt']
|
13
13
|
s.email = %q{rany@soundcloud.com, tobi@soundcloud.com, ts@soundcloud.com}
|
14
14
|
s.summary = %q{online schema changer for mysql}
|
15
15
|
s.description = %q{Migrate large tables without downtime by copying to a temporary table in chunks. The old table is not dropped. Instead, it is moved to timestamp_table_name for verification.}
|
16
16
|
s.homepage = %q{http://github.com/soundcloud/lhm}
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
-
s.require_paths = [
|
20
|
-
s.executables = [
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
s.executables = ['lhm-kill-queue']
|
21
21
|
|
22
|
-
s.add_development_dependency
|
23
|
-
s.add_development_dependency
|
22
|
+
s.add_development_dependency 'minitest', '~> 5.0.8'
|
23
|
+
s.add_development_dependency 'rake'
|
24
24
|
end
|
data/lib/lhm.rb
CHANGED
@@ -51,9 +51,22 @@ module Lhm
|
|
51
51
|
true
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
# Cleanup tables and triggers
|
55
|
+
#
|
56
|
+
# @param [Boolean] run execute now or just display information
|
57
|
+
# @param [Hash] options Optional options to alter cleanup behaviour
|
58
|
+
# @option options [Time] :until
|
59
|
+
# Filter to only remove tables up to specified time (defaults to: nil)
|
60
|
+
def cleanup(run = false, options = {})
|
61
|
+
lhm_tables = connection.select_values('show tables').select { |name| name =~ /^lhm(a|n)_/ }
|
62
|
+
if options[:until]
|
63
|
+
lhm_tables.select! { |table|
|
64
|
+
table_date_time = Time.strptime(table, 'lhma_%Y_%m_%d_%H_%M_%S')
|
65
|
+
table_date_time <= options[:until]
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
lhm_triggers = connection.select_values('show triggers').collect do |trigger|
|
57
70
|
trigger.respond_to?(:trigger) ? trigger.trigger : trigger
|
58
71
|
end.select { |name| name =~ /^lhmt/ }
|
59
72
|
|
@@ -66,12 +79,12 @@ module Lhm
|
|
66
79
|
end
|
67
80
|
true
|
68
81
|
elsif lhm_tables.empty? && lhm_triggers.empty?
|
69
|
-
puts
|
82
|
+
puts 'Everything is clean. Nothing to do.'
|
70
83
|
true
|
71
84
|
else
|
72
|
-
puts "Existing LHM backup tables: #{lhm_tables.join(
|
73
|
-
puts "Existing LHM triggers: #{lhm_triggers.join(
|
74
|
-
puts
|
85
|
+
puts "Existing LHM backup tables: #{lhm_tables.join(', ')}."
|
86
|
+
puts "Existing LHM triggers: #{lhm_triggers.join(', ')}."
|
87
|
+
puts 'Run Lhm.cleanup(true) to drop them all.'
|
75
88
|
false
|
76
89
|
end
|
77
90
|
end
|
@@ -107,5 +120,4 @@ module Lhm
|
|
107
120
|
def connection
|
108
121
|
Connection.new(adapter)
|
109
122
|
end
|
110
|
-
|
111
123
|
end
|
data/lib/lhm/atomic_switcher.rb
CHANGED
@@ -29,7 +29,7 @@ module Lhm
|
|
29
29
|
|
30
30
|
def atomic_switch
|
31
31
|
[
|
32
|
-
"rename table `#{ @origin.name }` to `#{ @migration.archive_name }`, "
|
32
|
+
"rename table `#{ @origin.name }` to `#{ @migration.archive_name }`, " \
|
33
33
|
"`#{ @destination.name }` to `#{ @origin.name }`"
|
34
34
|
]
|
35
35
|
end
|
@@ -42,6 +42,7 @@ module Lhm
|
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
|
+
|
45
46
|
def execute
|
46
47
|
@connection.sql(statements)
|
47
48
|
end
|
data/lib/lhm/chunker.rb
CHANGED
@@ -25,7 +25,7 @@ module Lhm
|
|
25
25
|
def execute
|
26
26
|
return unless @start && @limit
|
27
27
|
@next_to_insert = @start
|
28
|
-
|
28
|
+
while @next_to_insert < @limit || (@next_to_insert == 1 && @start == 1)
|
29
29
|
stride = @throttler.stride
|
30
30
|
affected_rows = @connection.update(copy(bottom, top(stride)))
|
31
31
|
|
@@ -50,8 +50,8 @@ module Lhm
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def copy(lowest, highest)
|
53
|
-
"insert ignore into `#{ destination_name }` (#{
|
54
|
-
"select #{
|
53
|
+
"insert ignore into `#{ destination_name }` (#{ destination_columns }) " \
|
54
|
+
"select #{ origin_columns } from `#{ origin_name }` " \
|
55
55
|
"#{ conditions } `#{ origin_name }`.`id` between #{ lowest } and #{ highest }"
|
56
56
|
end
|
57
57
|
|
@@ -65,22 +65,22 @@ module Lhm
|
|
65
65
|
limit ? limit.to_i : nil
|
66
66
|
end
|
67
67
|
|
68
|
-
#XXX this is extremely brittle and doesn't work when filter contains more
|
69
|
-
#than one SQL clause, e.g. "where ... group by foo". Before making any
|
70
|
-
#more changes here, please consider either:
|
68
|
+
# XXX this is extremely brittle and doesn't work when filter contains more
|
69
|
+
# than one SQL clause, e.g. "where ... group by foo". Before making any
|
70
|
+
# more changes here, please consider either:
|
71
71
|
#
|
72
|
-
#1. Letting users only specify part of defined clauses (i.e. don't allow
|
73
|
-
|
74
|
-
#2. Changing query building so that it uses structured data rather than
|
75
|
-
#strings until the last possible moment.
|
72
|
+
# 1. Letting users only specify part of defined clauses (i.e. don't allow
|
73
|
+
# `filter` on Migrator to accept both WHERE and INNER JOIN
|
74
|
+
# 2. Changing query building so that it uses structured data rather than
|
75
|
+
# strings until the last possible moment.
|
76
76
|
def conditions
|
77
77
|
if @migration.conditions
|
78
78
|
@migration.conditions.
|
79
|
-
sub(/\)\Z/,
|
80
|
-
#put any where conditions in parens
|
81
|
-
sub(/where\s(\w.*)\Z/,
|
79
|
+
sub(/\)\Z/, '').
|
80
|
+
# put any where conditions in parens
|
81
|
+
sub(/where\s(\w.*)\Z/, 'where (\\1)') + ' and'
|
82
82
|
else
|
83
|
-
|
83
|
+
'where'
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -92,17 +92,17 @@ module Lhm
|
|
92
92
|
@migration.origin.name
|
93
93
|
end
|
94
94
|
|
95
|
-
def
|
96
|
-
@
|
95
|
+
def origin_columns
|
96
|
+
@origin_columns ||= @migration.intersection.origin.typed(origin_name)
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
100
|
-
@
|
99
|
+
def destination_columns
|
100
|
+
@destination_columns ||= @migration.intersection.destination.joined
|
101
101
|
end
|
102
102
|
|
103
103
|
def validate
|
104
104
|
if @start && @limit && @start > @limit
|
105
|
-
error(
|
105
|
+
error('impossible chunk options (limit must be greater than start)')
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
data/lib/lhm/command.rb
CHANGED
data/lib/lhm/connection.rb
CHANGED
@@ -77,6 +77,14 @@ module Lhm
|
|
77
77
|
and table_name = '#{ table_name }'
|
78
78
|
})
|
79
79
|
end
|
80
|
+
|
81
|
+
def quote_value(value)
|
82
|
+
quoter.quote_value(value)
|
83
|
+
end
|
84
|
+
|
85
|
+
def quoter
|
86
|
+
@quoter ||= Object.new.tap { |o| o.extend(DataObjects::Quoting) }
|
87
|
+
end
|
80
88
|
end
|
81
89
|
|
82
90
|
class ActiveRecordConnection
|
@@ -138,6 +146,10 @@ module Lhm
|
|
138
146
|
def table_exists?(table_name)
|
139
147
|
@adapter.table_exists?(table_name)
|
140
148
|
end
|
149
|
+
|
150
|
+
def quote_value(value)
|
151
|
+
@adapter.quote(value)
|
152
|
+
end
|
141
153
|
end
|
142
154
|
end
|
143
155
|
end
|
data/lib/lhm/entangler.rb
CHANGED
@@ -14,7 +14,7 @@ module Lhm
|
|
14
14
|
# Creates entanglement between two tables. All creates, updates and deletes
|
15
15
|
# to origin will be repeated on the destination table.
|
16
16
|
def initialize(migration, connection = nil)
|
17
|
-
@
|
17
|
+
@intersection = migration.intersection
|
18
18
|
@origin = migration.origin
|
19
19
|
@destination = migration.destination
|
20
20
|
@connection = connection
|
@@ -40,8 +40,8 @@ module Lhm
|
|
40
40
|
strip %Q{
|
41
41
|
create trigger `#{ trigger(:ins) }`
|
42
42
|
after insert on `#{ @origin.name }` for each row
|
43
|
-
replace into `#{ @destination.name }` (#{ @
|
44
|
-
values (#{ @
|
43
|
+
replace into `#{ @destination.name }` (#{ @intersection.destination.joined }) #{ SqlHelper.annotation }
|
44
|
+
values (#{ @intersection.origin.typed('NEW') })
|
45
45
|
}
|
46
46
|
end
|
47
47
|
|
@@ -49,8 +49,8 @@ module Lhm
|
|
49
49
|
strip %Q{
|
50
50
|
create trigger `#{ trigger(:upd) }`
|
51
51
|
after update on `#{ @origin.name }` for each row
|
52
|
-
replace into `#{ @destination.name }` (#{ @
|
53
|
-
values (#{ @
|
52
|
+
replace into `#{ @destination.name }` (#{ @intersection.destination.joined }) #{ SqlHelper.annotation }
|
53
|
+
values (#{ @intersection.origin.typed('NEW') })
|
54
54
|
}
|
55
55
|
end
|
56
56
|
|
data/lib/lhm/intersection.rb
CHANGED
@@ -4,35 +4,48 @@
|
|
4
4
|
module Lhm
|
5
5
|
# Determine and format columns common to origin and destination.
|
6
6
|
class Intersection
|
7
|
-
def initialize(origin, destination)
|
7
|
+
def initialize(origin, destination, renames = {})
|
8
8
|
@origin = origin
|
9
9
|
@destination = destination
|
10
|
+
@renames = renames
|
10
11
|
end
|
11
12
|
|
12
|
-
def
|
13
|
-
(
|
13
|
+
def origin
|
14
|
+
(common + @renames.keys).extend(Joiners)
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
common
|
17
|
+
def destination
|
18
|
+
(common + @renames.values).extend(Joiners)
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
escaped.join(", ")
|
22
|
-
end
|
21
|
+
private
|
23
22
|
|
24
|
-
def
|
25
|
-
|
23
|
+
def common
|
24
|
+
(@origin.columns.keys & @destination.columns.keys).sort
|
26
25
|
end
|
27
26
|
|
28
|
-
|
27
|
+
module Joiners
|
28
|
+
def escaped
|
29
|
+
self.map { |name| tick(name) }
|
30
|
+
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def joined
|
33
|
+
escaped.join(', ')
|
34
|
+
end
|
35
|
+
|
36
|
+
def typed(type)
|
37
|
+
self.map { |name| qualified(name, type) }.join(', ')
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def qualified(name, type)
|
43
|
+
"`#{ type }`.`#{ name }`"
|
44
|
+
end
|
33
45
|
|
34
|
-
|
35
|
-
|
46
|
+
def tick(name)
|
47
|
+
"`#{ name }`"
|
48
|
+
end
|
36
49
|
end
|
37
50
|
end
|
38
51
|
end
|
data/lib/lhm/invoker.rb
CHANGED
@@ -47,8 +47,8 @@ module Lhm
|
|
47
47
|
options[:atomic_switch] = true
|
48
48
|
else
|
49
49
|
raise Error.new(
|
50
|
-
"Using mysql #{version_string}. You must explicitly set "
|
51
|
-
|
50
|
+
"Using mysql #{version_string}. You must explicitly set " \
|
51
|
+
'options[:atomic_switch] (re SqlHelper#supports_atomic_switch?)')
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -56,7 +56,7 @@ module Lhm
|
|
56
56
|
options[:throttler] = Throttler::Factory.create_throttler(*options[:throttler])
|
57
57
|
elsif options[:throttle] || options[:stride]
|
58
58
|
# we still support the throttle and stride as a Fixnum input
|
59
|
-
warn
|
59
|
+
warn 'throttle option will no longer accept a Fixnum in the next versions.'
|
60
60
|
options[:throttler] = Throttler::LegacyTime.new(options[:throttle], options[:stride])
|
61
61
|
else
|
62
62
|
options[:throttler] = Lhm.throttler
|
data/lib/lhm/locked_switcher.rb
CHANGED
@@ -38,17 +38,17 @@ module Lhm
|
|
38
38
|
"lock table `#{ @origin.name }` write, `#{ @destination.name }` write",
|
39
39
|
"alter table `#{ @origin.name }` rename `#{ @migration.archive_name }`",
|
40
40
|
"alter table `#{ @destination.name }` rename `#{ @origin.name }`",
|
41
|
-
|
42
|
-
|
41
|
+
'commit',
|
42
|
+
'unlock tables'
|
43
43
|
]
|
44
44
|
end
|
45
45
|
|
46
46
|
def uncommitted(&block)
|
47
47
|
[
|
48
|
-
|
49
|
-
|
48
|
+
'set @lhm_auto_commit = @@session.autocommit',
|
49
|
+
'set session autocommit = 0',
|
50
50
|
yield,
|
51
|
-
|
51
|
+
'set session autocommit = @lhm_auto_commit'
|
52
52
|
].flatten
|
53
53
|
end
|
54
54
|
|
@@ -62,7 +62,7 @@ module Lhm
|
|
62
62
|
private
|
63
63
|
|
64
64
|
def revert
|
65
|
-
@connection.sql(
|
65
|
+
@connection.sql('unlock tables')
|
66
66
|
end
|
67
67
|
|
68
68
|
def execute
|
data/lib/lhm/migration.rb
CHANGED
@@ -5,13 +5,14 @@ require 'lhm/intersection'
|
|
5
5
|
|
6
6
|
module Lhm
|
7
7
|
class Migration
|
8
|
-
attr_reader :origin, :destination, :conditions
|
8
|
+
attr_reader :origin, :destination, :conditions, :renames
|
9
9
|
|
10
|
-
def initialize(origin, destination, conditions = nil, time = Time.now)
|
10
|
+
def initialize(origin, destination, conditions = nil, renames = {}, time = Time.now)
|
11
11
|
@origin = origin
|
12
12
|
@destination = destination
|
13
13
|
@conditions = conditions
|
14
14
|
@start = time
|
15
|
+
@renames = renames
|
15
16
|
end
|
16
17
|
|
17
18
|
def archive_name
|
@@ -19,11 +20,11 @@ module Lhm
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def intersection
|
22
|
-
Intersection.new(@origin, @destination)
|
23
|
+
Intersection.new(@origin, @destination, @renames)
|
23
24
|
end
|
24
25
|
|
25
26
|
def startstamp
|
26
|
-
@start.strftime "%Y_%m_%d_%H_%M_%S_#{
|
27
|
+
@start.strftime "%Y_%m_%d_%H_%M_%S_#{ '%03d' % (@start.usec / 1000) }"
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
data/lib/lhm/migrator.rb
CHANGED
@@ -13,13 +13,14 @@ module Lhm
|
|
13
13
|
include Command
|
14
14
|
include SqlHelper
|
15
15
|
|
16
|
-
attr_reader :name, :statements, :connection, :conditions
|
16
|
+
attr_reader :name, :statements, :connection, :conditions, :renames
|
17
17
|
|
18
18
|
def initialize(table, connection = nil)
|
19
19
|
@connection = connection
|
20
20
|
@origin = table
|
21
21
|
@name = table.destination_name
|
22
22
|
@statements = []
|
23
|
+
@renames = {}
|
23
24
|
end
|
24
25
|
|
25
26
|
# Alter a table with a custom statement
|
@@ -52,7 +53,7 @@ module Lhm
|
|
52
53
|
# @param [String] name Name of the column to add
|
53
54
|
# @param [String] definition Valid SQL column definition
|
54
55
|
def add_column(name, definition)
|
55
|
-
ddl(
|
56
|
+
ddl('alter table `%s` add column `%s` %s' % [@name, name, definition])
|
56
57
|
end
|
57
58
|
|
58
59
|
# Change an existing column to a new definition
|
@@ -66,7 +67,28 @@ module Lhm
|
|
66
67
|
# @param [String] name Name of the column to change
|
67
68
|
# @param [String] definition Valid SQL column definition
|
68
69
|
def change_column(name, definition)
|
69
|
-
ddl(
|
70
|
+
ddl('alter table `%s` modify column `%s` %s' % [@name, name, definition])
|
71
|
+
end
|
72
|
+
|
73
|
+
# Rename an existing column.
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
#
|
77
|
+
# Lhm.change_table(:users) do |m|
|
78
|
+
# m.rename_column(:login, :username)
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @param [String] old Name of the column to change
|
82
|
+
# @param [String] nu New name to use for the column
|
83
|
+
def rename_column(old, nu)
|
84
|
+
col = @origin.columns[old.to_s]
|
85
|
+
|
86
|
+
definition = col[:type]
|
87
|
+
definition += ' NOT NULL' unless col[:is_nullable]
|
88
|
+
definition += " DEFAULT #{@connection.quote_value(col[:column_default])}" if col[:column_default]
|
89
|
+
|
90
|
+
ddl('alter table `%s` change column `%s` `%s` %s' % [@name, old, nu, definition])
|
91
|
+
@renames[old.to_s] = nu.to_s
|
70
92
|
end
|
71
93
|
|
72
94
|
# Remove a column from a table
|
@@ -79,7 +101,7 @@ module Lhm
|
|
79
101
|
#
|
80
102
|
# @param [String] name Name of the column to delete
|
81
103
|
def remove_column(name)
|
82
|
-
ddl(
|
104
|
+
ddl('alter table `%s` drop `%s`' % [@name, name])
|
83
105
|
end
|
84
106
|
|
85
107
|
# Add an index to a table
|
@@ -136,10 +158,10 @@ module Lhm
|
|
136
158
|
# Optional name of the index to be removed
|
137
159
|
def remove_index(columns, index_name = nil)
|
138
160
|
columns = [columns].flatten.map(&:to_sym)
|
139
|
-
from_origin = @origin.indices.find {|name, cols| cols.map(&:to_sym) == columns}
|
161
|
+
from_origin = @origin.indices.find { |name, cols| cols.map(&:to_sym) == columns }
|
140
162
|
index_name ||= from_origin[0] unless from_origin.nil?
|
141
163
|
index_name ||= idx_name(@origin.name, columns)
|
142
|
-
ddl(
|
164
|
+
ddl('drop index `%s` on `%s`' % [index_name, @name])
|
143
165
|
end
|
144
166
|
|
145
167
|
# Filter the data that is copied into the new table by the provided SQL.
|
@@ -166,7 +188,7 @@ module Lhm
|
|
166
188
|
end
|
167
189
|
|
168
190
|
unless @origin.satisfies_primary_key?
|
169
|
-
error(
|
191
|
+
error('origin does not satisfy primary key requirements')
|
170
192
|
end
|
171
193
|
|
172
194
|
dest = @origin.destination_name
|
@@ -179,7 +201,7 @@ module Lhm
|
|
179
201
|
def execute
|
180
202
|
destination_create
|
181
203
|
@connection.sql(@statements)
|
182
|
-
Migration.new(@origin, destination_read, conditions)
|
204
|
+
Migration.new(@origin, destination_read, conditions, renames)
|
183
205
|
end
|
184
206
|
|
185
207
|
def destination_create
|
@@ -191,10 +213,17 @@ module Lhm
|
|
191
213
|
end
|
192
214
|
|
193
215
|
def index_ddl(cols, unique = nil, index_name = nil)
|
194
|
-
|
216
|
+
assert_valid_idx_name(index_name)
|
217
|
+
type = unique ? 'unique index' : 'index'
|
195
218
|
index_name ||= idx_name(@origin.name, cols)
|
196
219
|
parts = [type, index_name, @name, idx_spec(cols)]
|
197
|
-
|
220
|
+
'create %s `%s` on `%s` (%s)' % parts
|
221
|
+
end
|
222
|
+
|
223
|
+
def assert_valid_idx_name(index_name)
|
224
|
+
if index_name && !(index_name.is_a?(String) || index_name.is_a?(Symbol))
|
225
|
+
raise ArgumentError, 'index_name must be a string or symbol'
|
226
|
+
end
|
198
227
|
end
|
199
228
|
end
|
200
229
|
end
|