departure 4.0.1 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +8 -0
- data/.gitignore +1 -0
- data/.pryrc +11 -0
- data/.rubocop.yml +60 -0
- data/.travis.yml +19 -2
- data/CHANGELOG.md +56 -0
- data/Dockerfile +32 -0
- data/Gemfile +2 -1
- data/LICENSE.txt +2 -1
- data/README.md +56 -15
- data/RELEASING.md +1 -1
- data/bin/console +3 -3
- data/bin/rspec +6 -7
- data/config.yml.erb +4 -0
- data/configuration.rb +3 -2
- data/departure.gemspec +14 -9
- data/docker-compose.yml +23 -0
- data/lib/active_record/connection_adapters/for_alter.rb +91 -0
- data/lib/active_record/connection_adapters/percona_adapter.rb +75 -17
- data/lib/departure.rb +11 -49
- data/lib/departure/alter_argument.rb +10 -4
- data/lib/departure/cli_generator.rb +2 -3
- data/lib/departure/command.rb +3 -3
- data/lib/departure/configuration.rb +2 -1
- data/lib/departure/connection_base.rb +9 -0
- data/lib/departure/connection_details.rb +29 -3
- data/lib/departure/dsn.rb +0 -2
- data/lib/departure/log_sanitizers/password_sanitizer.rb +2 -1
- data/lib/departure/logger.rb +1 -2
- data/lib/departure/logger_factory.rb +0 -1
- data/lib/departure/migration.rb +104 -0
- data/lib/departure/option.rb +4 -4
- data/lib/departure/railtie.rb +6 -13
- data/lib/departure/runner.rb +0 -2
- data/lib/departure/version.rb +1 -1
- data/lib/lhm.rb +1 -3
- data/lib/lhm/adapter.rb +1 -3
- data/lib/lhm/column_with_sql.rb +8 -4
- data/lib/lhm/column_with_type.rb +0 -2
- data/test_database.rb +37 -9
- metadata +55 -33
- data/config.yml +0 -3
data/departure.gemspec
CHANGED
@@ -1,30 +1,35 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
|
5
6
|
require 'departure/version'
|
6
7
|
|
8
|
+
# This environment variable is set on CI to facilitate testing with multiple
|
9
|
+
# versions of Rails.
|
10
|
+
RAILS_DEPENDENCY_VERSION = ENV.fetch('RAILS_VERSION', ['>= 5.2.0', '<= 6.1'])
|
11
|
+
|
7
12
|
Gem::Specification.new do |spec|
|
8
13
|
spec.name = 'departure'
|
9
14
|
spec.version = Departure::VERSION
|
10
|
-
spec.authors = ['Ilya Zayats', 'Pau Pérez', 'Fran Casas', 'Jorge Morante', 'Enrico Stano', 'Adrian Serafin']
|
11
|
-
spec.email = ['ilya.zayats@redbooth.com', 'pau.perez@redbooth.com', '
|
15
|
+
spec.authors = ['Ilya Zayats', 'Pau Pérez', 'Fran Casas', 'Jorge Morante', 'Enrico Stano', 'Adrian Serafin', 'Kirk Haines', 'Guillermo Iguaran']
|
16
|
+
spec.email = ['ilya.zayats@redbooth.com', 'pau.perez@redbooth.com', 'nflamel@gmail.com', 'jorge.morante@redbooth.com', 'adrian@softmad.pl', 'wyhaines@gmail.com', 'guilleiguaran@gmail.com']
|
12
17
|
|
13
|
-
spec.summary = %q
|
14
|
-
spec.description = %q
|
15
|
-
spec.homepage = '
|
18
|
+
spec.summary = %q(pt-online-schema-change runner for ActiveRecord migrations)
|
19
|
+
spec.description = %q(Execute your ActiveRecord migrations with Percona's pt-online-schema-change. Formerly known as Percona Migrator.)
|
20
|
+
spec.homepage = 'https://github.com/departurerb/departure'
|
16
21
|
spec.license = 'MIT'
|
17
22
|
|
18
23
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
24
|
spec.require_paths = ['lib']
|
20
25
|
|
21
|
-
spec.add_runtime_dependency '
|
22
|
-
spec.add_runtime_dependency '
|
26
|
+
spec.add_runtime_dependency 'railties', *Array(RAILS_DEPENDENCY_VERSION)
|
27
|
+
spec.add_runtime_dependency 'activerecord', *Array(RAILS_DEPENDENCY_VERSION)
|
28
|
+
spec.add_runtime_dependency 'mysql2', '>= 0.4.0', '<= 0.5.3'
|
23
29
|
|
24
|
-
spec.add_development_dependency 'bundler', '~> 1.10'
|
25
30
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
31
|
spec.add_development_dependency 'rspec', '~> 3.4', '>= 3.4.0'
|
27
32
|
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
28
|
-
spec.add_development_dependency 'byebug'
|
33
|
+
spec.add_development_dependency 'pry-byebug'
|
29
34
|
spec.add_development_dependency 'climate_control', '~> 0.0.3'
|
30
35
|
end
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
version: '2'
|
2
|
+
services:
|
3
|
+
db:
|
4
|
+
image: mysql/mysql-server
|
5
|
+
ports:
|
6
|
+
- "3306:3306"
|
7
|
+
environment:
|
8
|
+
MYSQL_DATABASE: departure_test
|
9
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
10
|
+
MYSQL_ROOT_HOST: '%'
|
11
|
+
rails:
|
12
|
+
build: .
|
13
|
+
links:
|
14
|
+
- db
|
15
|
+
depends_on:
|
16
|
+
- db
|
17
|
+
tty: true
|
18
|
+
stdin_open: true
|
19
|
+
environment:
|
20
|
+
PERCONA_DB_USER: root
|
21
|
+
PERCONA_DB_HOST: db
|
22
|
+
PERCONA_DB_PASSWORD:
|
23
|
+
PERCONA_DB_NAME: departure_test
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'active_record/connection_adapters/mysql/schema_statements'
|
2
|
+
|
3
|
+
module ForAlterStatements
|
4
|
+
class << self
|
5
|
+
def included(_)
|
6
|
+
STDERR.puts 'Including for_alter statements'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def bulk_change_table(table_name, operations) #:nodoc:
|
11
|
+
sqls = operations.flat_map do |command, args|
|
12
|
+
table = args.shift
|
13
|
+
arguments = args
|
14
|
+
|
15
|
+
method = :"#{command}_for_alter"
|
16
|
+
|
17
|
+
raise "Unknown method called : #{method}(#{arguments.inspect})" unless respond_to?(method, true)
|
18
|
+
public_send(method, table, *arguments)
|
19
|
+
end.join(', ')
|
20
|
+
|
21
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
25
|
+
column = column_for(table_name, column_name)
|
26
|
+
type ||= column.sql_type
|
27
|
+
|
28
|
+
options = {
|
29
|
+
default: column.default,
|
30
|
+
null: column.null,
|
31
|
+
comment: column.comment
|
32
|
+
}.merge(options)
|
33
|
+
|
34
|
+
td = create_table_definition(table_name)
|
35
|
+
cd = td.new_column_definition(column.name, type, options)
|
36
|
+
schema_creation.accept(ActiveRecord::ConnectionAdapters::ChangeColumnDefinition.new(cd, column.name))
|
37
|
+
end
|
38
|
+
|
39
|
+
def rename_column_for_alter(table_name, column_name, new_column_name)
|
40
|
+
column = column_for(table_name, column_name)
|
41
|
+
options = {
|
42
|
+
default: column.default,
|
43
|
+
null: column.null,
|
44
|
+
auto_increment: column.auto_increment?
|
45
|
+
}
|
46
|
+
|
47
|
+
columns_sql = "SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}"
|
48
|
+
current_type = exec_query(columns_sql, 'SCHEMA').first['Type']
|
49
|
+
td = create_table_definition(table_name)
|
50
|
+
cd = td.new_column_definition(new_column_name, current_type, options)
|
51
|
+
schema_creation.accept(ActiveRecord::ConnectionAdapters::ChangeColumnDefinition.new(cd, column.name))
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_index_for_alter(table_name, column_name, options = {})
|
55
|
+
index_name, index_type, index_columns, _,
|
56
|
+
index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
57
|
+
|
58
|
+
index_algorithm[0, 0] = ', ' if index_algorithm.present?
|
59
|
+
"ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_index_for_alter(table_name, options = {})
|
63
|
+
index_name = index_name_for_remove(table_name, options)
|
64
|
+
"DROP INDEX #{quote_column_name(index_name)}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_timestamps_for_alter(table_name, options = {})
|
68
|
+
[
|
69
|
+
add_column_for_alter(table_name, :created_at, :datetime, options),
|
70
|
+
add_column_for_alter(table_name, :updated_at, :datetime, options)
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_timestamps_for_alter(table_name, _options = {})
|
75
|
+
[remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_column_for_alter(table_name, column_name, type, options = {})
|
79
|
+
td = create_table_definition(table_name)
|
80
|
+
cd = td.new_column_definition(column_name, type, options)
|
81
|
+
schema_creation.accept(ActiveRecord::ConnectionAdapters::AddColumnDefinition.new(cd))
|
82
|
+
end
|
83
|
+
|
84
|
+
def remove_column_for_alter(_table_name, column_name, _type = nil, _options = {})
|
85
|
+
"DROP COLUMN #{quote_column_name(column_name)}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def remove_columns_for_alter(table_name, *column_names)
|
89
|
+
column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
|
90
|
+
end
|
91
|
+
end
|
@@ -9,10 +9,12 @@ module ActiveRecord
|
|
9
9
|
# Establishes a connection to the database that's used by all Active
|
10
10
|
# Record objects.
|
11
11
|
def percona_connection(config)
|
12
|
+
if config[:username].nil?
|
13
|
+
config = config.dup if config.frozen?
|
14
|
+
config[:username] = 'root'
|
15
|
+
end
|
12
16
|
mysql2_connection = mysql2_connection(config)
|
13
17
|
|
14
|
-
config[:username] = 'root' if config[:username].nil?
|
15
|
-
|
16
18
|
connection_details = Departure::ConnectionDetails.new(config)
|
17
19
|
verbose = ActiveRecord::Migration.verbose
|
18
20
|
sanitizers = [
|
@@ -40,32 +42,54 @@ module ActiveRecord
|
|
40
42
|
|
41
43
|
module ConnectionAdapters
|
42
44
|
class DepartureAdapter < AbstractMysqlAdapter
|
43
|
-
|
44
|
-
class Column < AbstractMysqlAdapter::Column
|
45
|
+
class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
|
45
46
|
def adapter
|
46
47
|
DepartureAdapter
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
51
|
+
class SchemaCreation < ActiveRecord::ConnectionAdapters::MySQL::SchemaCreation
|
52
|
+
def visit_DropForeignKey(name) # rubocop:disable Naming/MethodName
|
53
|
+
fk_name =
|
54
|
+
if name =~ /^__(.+)/
|
55
|
+
Regexp.last_match(1)
|
56
|
+
else
|
57
|
+
"_#{name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
"DROP FOREIGN KEY #{fk_name}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
50
64
|
extend Forwardable
|
51
65
|
|
66
|
+
unless method_defined?(:change_column_for_alter)
|
67
|
+
include ForAlterStatements
|
68
|
+
end
|
69
|
+
|
52
70
|
ADAPTER_NAME = 'Percona'.freeze
|
53
71
|
|
54
72
|
def_delegators :mysql_adapter, :last_inserted_id, :each_hash, :set_field_encoding
|
55
73
|
|
56
74
|
def initialize(connection, _logger, connection_options, _config)
|
75
|
+
@mysql_adapter = connection_options[:mysql_adapter]
|
57
76
|
super
|
58
77
|
@prepared_statements = false
|
59
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_query?(sql) # :nodoc:
|
81
|
+
!ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
82
|
+
:desc, :describe, :set, :show, :use
|
83
|
+
).match?(sql)
|
60
84
|
end
|
61
85
|
|
62
86
|
def exec_delete(sql, name, binds)
|
63
87
|
execute(to_sql(sql, binds), name)
|
64
88
|
@connection.affected_rows
|
65
89
|
end
|
66
|
-
alias
|
90
|
+
alias exec_update exec_delete
|
67
91
|
|
68
|
-
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
92
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # rubocop:disable Lint/UnusedMethodArgument, Metrics/LineLength
|
69
93
|
execute(to_sql(sql, binds), name)
|
70
94
|
end
|
71
95
|
|
@@ -76,8 +100,9 @@ module ActiveRecord
|
|
76
100
|
|
77
101
|
# Executes a SELECT query and returns an array of rows. Each row is an
|
78
102
|
# array of field values.
|
79
|
-
|
80
|
-
|
103
|
+
|
104
|
+
def select_rows(arel, name = nil, binds = [])
|
105
|
+
select_all(arel, name, binds).rows
|
81
106
|
end
|
82
107
|
|
83
108
|
# Executes a SELECT query and returns an array of record hashes with the
|
@@ -91,9 +116,11 @@ module ActiveRecord
|
|
91
116
|
true
|
92
117
|
end
|
93
118
|
|
94
|
-
|
95
|
-
|
119
|
+
# rubocop:disable Metrics/ParameterLists
|
120
|
+
def new_column(field, default, type_metadata, null, table_name, default_function, collation, comment)
|
121
|
+
Column.new(field, default, type_metadata, null, table_name, default_function, collation, comment)
|
96
122
|
end
|
123
|
+
# rubocop:enable Metrics/ParameterLists
|
97
124
|
|
98
125
|
# Adds a new index to the table
|
99
126
|
#
|
@@ -101,25 +128,56 @@ module ActiveRecord
|
|
101
128
|
# @param column_name [String, Symbol]
|
102
129
|
# @param options [Hash] optional
|
103
130
|
def add_index(table_name, column_name, options = {})
|
104
|
-
|
105
|
-
|
131
|
+
if ActiveRecord::VERSION::STRING >= '6.1'
|
132
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, options)
|
133
|
+
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
134
|
+
execute schema_creation.accept(create_index)
|
135
|
+
else
|
136
|
+
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
137
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ADD #{index_type} INDEX #{quote_column_name(index_name)} (#{index_columns})#{index_options}" # rubocop:disable Metrics/LineLength
|
138
|
+
end
|
106
139
|
end
|
107
140
|
|
108
141
|
# Remove the given index from the table.
|
109
142
|
#
|
110
143
|
# @param table_name [String, Symbol]
|
111
144
|
# @param options [Hash] optional
|
112
|
-
def remove_index(table_name, options
|
113
|
-
|
145
|
+
def remove_index(table_name, *args, **options)
|
146
|
+
column_name = args.first
|
147
|
+
if column_name
|
148
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
149
|
+
index_name = index_name_for_remove(table_name, column_name, **options)
|
150
|
+
else
|
151
|
+
index_name = index_name_for_remove(table_name, options)
|
152
|
+
end
|
114
153
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP INDEX #{quote_column_name(index_name)}"
|
115
154
|
end
|
116
155
|
|
156
|
+
def schema_creation
|
157
|
+
SchemaCreation.new(self)
|
158
|
+
end
|
159
|
+
|
160
|
+
def change_table(table_name, _options = {})
|
161
|
+
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
162
|
+
yield update_table_definition(table_name, recorder)
|
163
|
+
bulk_change_table(table_name, recorder.commands)
|
164
|
+
end
|
165
|
+
|
117
166
|
# Returns the MySQL error number from the exception. The
|
118
167
|
# AbstractMysqlAdapter requires it to be implemented
|
119
|
-
def error_number(_exception)
|
120
|
-
end
|
168
|
+
def error_number(_exception); end
|
121
169
|
|
122
170
|
def full_version
|
171
|
+
if ActiveRecord::VERSION::MAJOR < 6
|
172
|
+
get_full_version
|
173
|
+
else
|
174
|
+
schema_cache.database_version.full_version_string
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# This is a method defined in Rails 6.0, and we have no control over the
|
179
|
+
# naming of this method.
|
180
|
+
def get_full_version # rubocop:disable Naming/AccessorMethodName
|
123
181
|
mysql_adapter.raw_connection.server_info[:version]
|
124
182
|
end
|
125
183
|
|
data/lib/departure.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
require 'active_support/all'
|
3
3
|
|
4
|
+
require 'active_record/connection_adapters/for_alter'
|
5
|
+
|
4
6
|
require 'departure/version'
|
5
7
|
require 'departure/log_sanitizers/password_sanitizer'
|
6
8
|
require 'departure/runner'
|
@@ -11,12 +13,20 @@ require 'departure/logger_factory'
|
|
11
13
|
require 'departure/configuration'
|
12
14
|
require 'departure/errors'
|
13
15
|
require 'departure/command'
|
16
|
+
require 'departure/connection_base'
|
17
|
+
require 'departure/migration'
|
14
18
|
|
15
19
|
require 'departure/railtie' if defined?(Rails)
|
16
20
|
|
17
21
|
# We need the OS not to buffer the IO to see pt-osc's output while migrating
|
18
22
|
$stdout.sync = true
|
19
23
|
|
24
|
+
ActiveSupport.on_load(:active_record) do
|
25
|
+
ActiveRecord::Migration.class_eval do
|
26
|
+
include Departure::Migration
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
module Departure
|
21
31
|
class << self
|
22
32
|
attr_accessor :configuration
|
@@ -27,55 +37,7 @@ module Departure
|
|
27
37
|
yield(configuration)
|
28
38
|
end
|
29
39
|
|
30
|
-
# Hooks Percona Migrator into Rails migrations by replacing the configured
|
31
|
-
# database adapter
|
32
40
|
def self.load
|
33
|
-
|
34
|
-
class << self
|
35
|
-
alias_method(:original_migrate, :migrate)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Checks whether arguments are being passed through PERCONA_ARGS when running
|
39
|
-
# the db:migrate rake task
|
40
|
-
#
|
41
|
-
# @raise [ArgumentsNotSupported] if PERCONA_ARGS has any value
|
42
|
-
def migrate(migrations_paths, target_version = nil, &block)
|
43
|
-
raise ArgumentsNotSupported if ENV['PERCONA_ARGS'].present?
|
44
|
-
original_migrate(migrations_paths, target_version, &block)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
ActiveRecord::Migration.class_eval do
|
49
|
-
alias_method :original_migrate, :migrate
|
50
|
-
|
51
|
-
# Replaces the current connection adapter with the PerconaAdapter and
|
52
|
-
# patches LHM, then it continues with the regular migration process.
|
53
|
-
#
|
54
|
-
# @param direction [Symbol] :up or :down
|
55
|
-
def migrate(direction)
|
56
|
-
reconnect_with_percona
|
57
|
-
include_foreigner if defined?(Foreigner)
|
58
|
-
|
59
|
-
::Lhm.migration = self
|
60
|
-
original_migrate(direction)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Includes the Foreigner's Mysql2Adapter implemention in
|
64
|
-
# DepartureAdapter to support foreign keys
|
65
|
-
def include_foreigner
|
66
|
-
Foreigner::Adapter.safe_include(
|
67
|
-
:DepartureAdapter,
|
68
|
-
Foreigner::ConnectionAdapters::Mysql2Adapter
|
69
|
-
)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Make all connections in the connection pool to use PerconaAdapter
|
73
|
-
# instead of the current adapter.
|
74
|
-
def reconnect_with_percona
|
75
|
-
connection_config = ActiveRecord::Base
|
76
|
-
.connection_config.merge(adapter: 'percona')
|
77
|
-
ActiveRecord::Base.establish_connection(connection_config)
|
78
|
-
end
|
79
|
-
end
|
41
|
+
# No-op left for compatibility
|
80
42
|
end
|
81
43
|
end
|
@@ -4,7 +4,7 @@ module Departure
|
|
4
4
|
# Represents the '--alter' argument of Percona's pt-online-schema-change
|
5
5
|
# See https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html
|
6
6
|
class AlterArgument
|
7
|
-
ALTER_TABLE_REGEX = /\AALTER TABLE
|
7
|
+
ALTER_TABLE_REGEX = /\AALTER TABLE [^\s]*[\n]* /
|
8
8
|
|
9
9
|
attr_reader :table_name
|
10
10
|
|
@@ -17,8 +17,12 @@ module Departure
|
|
17
17
|
|
18
18
|
match = statement.match(ALTER_TABLE_REGEX)
|
19
19
|
raise InvalidAlterStatement unless match
|
20
|
-
|
21
|
-
|
20
|
+
# Separates the ALTER TABLE from the table_name
|
21
|
+
#
|
22
|
+
# Removes the grave marks, if they are there, so we can get the table_name
|
23
|
+
@table_name = String(match)
|
24
|
+
.split(' ')[2]
|
25
|
+
.delete('`')
|
22
26
|
end
|
23
27
|
|
24
28
|
# Returns the '--alter' pt-online-schema-change argument as a string. See
|
@@ -37,7 +41,9 @@ module Departure
|
|
37
41
|
def parsed_statement
|
38
42
|
@parsed_statement ||= statement
|
39
43
|
.gsub(ALTER_TABLE_REGEX, '')
|
40
|
-
.gsub('`','\\\`')
|
44
|
+
.gsub('`', '\\\`')
|
45
|
+
.gsub(/\\n/, '')
|
46
|
+
.gsub('"', '\\\"')
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -5,12 +5,11 @@ require 'departure/connection_details'
|
|
5
5
|
require 'departure/user_options'
|
6
6
|
|
7
7
|
module Departure
|
8
|
-
|
9
8
|
# Generates the equivalent Percona's pt-online-schema-change command to the
|
10
9
|
# given SQL statement
|
11
10
|
#
|
12
|
-
# --no-check-alter is used to allow running CHANGE COLUMN statements. For
|
13
|
-
#
|
11
|
+
# --no-check-alter is used to allow running CHANGE COLUMN statements. For more details, check:
|
12
|
+
# www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html#cmdoption-pt-online-schema-change--[no]check-alter # rubocop:disable Metrics/LineLength
|
14
13
|
#
|
15
14
|
class CliGenerator
|
16
15
|
COMMAND_NAME = 'pt-online-schema-change'.freeze
|