departure 8.0.0 → 8.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 +4 -4
- data/.github/workflows/test.yml +41 -2
- data/.pryrc +6 -6
- data/.standard.yml +9 -0
- data/AGENTS.md +77 -0
- data/Appraisals +11 -8
- data/CHANGELOG.md +16 -0
- data/Gemfile +8 -9
- data/Gemfile.lock +25 -11
- data/README.md +26 -4
- data/RELEASING.md +5 -4
- data/Rakefile +10 -10
- data/configuration.rb +3 -3
- data/departure.gemspec +26 -29
- data/gemfiles/rails_7_2.gemfile +2 -2
- data/gemfiles/rails_7_2.gemfile.lock +28 -11
- data/gemfiles/rails_8_0.gemfile +2 -2
- data/gemfiles/rails_8_0.gemfile.lock +28 -11
- data/gemfiles/rails_8_1.gemfile +3 -3
- data/gemfiles/rails_8_1.gemfile.lock +27 -11
- data/lib/active_record/connection_adapters/departure_adapter_behavior.rb +88 -0
- data/lib/active_record/connection_adapters/for_alter.rb +4 -10
- data/lib/active_record/connection_adapters/patch_connection_handling.rb +7 -6
- data/lib/active_record/connection_adapters/rails_7_2_departure_adapter.rb +17 -58
- data/lib/active_record/connection_adapters/rails_7_2_trilogy_adapter.rb +53 -0
- data/lib/active_record/connection_adapters/rails_8_0_departure_adapter.rb +26 -66
- data/lib/active_record/connection_adapters/rails_8_0_trilogy_adapter.rb +21 -0
- data/lib/active_record/connection_adapters/rails_8_1_mysql2_adapter.rb +23 -0
- data/lib/active_record/connection_adapters/rails_8_1_trilogy_adapter.rb +21 -0
- data/lib/departure/alter_argument.rb +6 -6
- data/lib/departure/cli_generator.rb +12 -12
- data/lib/departure/command.rb +1 -1
- data/lib/departure/configuration.rb +3 -3
- data/lib/departure/connection_details.rb +7 -7
- data/lib/departure/db_client.rb +5 -5
- data/lib/departure/dsn.rb +2 -2
- data/lib/departure/errors.rb +2 -2
- data/lib/departure/log_sanitizers/password_sanitizer.rb +1 -1
- data/lib/departure/logger.rb +2 -2
- data/lib/departure/migration.rb +40 -10
- data/lib/departure/option.rb +4 -4
- data/lib/departure/rails_adapter.rb +145 -85
- data/lib/departure/rails_patches/active_record_migrator_with_advisory_lock_patch.rb +2 -2
- data/lib/departure/railtie.rb +7 -5
- data/lib/departure/runner.rb +3 -3
- data/lib/departure/user_options.rb +1 -1
- data/lib/departure/version.rb +1 -1
- data/lib/departure.rb +21 -23
- data/lib/lhm/adapter.rb +12 -12
- data/lib/lhm/column_with_sql.rb +8 -8
- data/lib/lhm.rb +1 -1
- data/test_database.rb +10 -10
- metadata +12 -4
- data/.rubocop.yml +0 -66
- data/.rubocop_todo.yml +0 -238
- data/lib/active_record/connection_adapters/rails_8_1_departure_adapter.rb +0 -83
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
1
|
+
require "active_record/connection_adapters/abstract_mysql_adapter"
|
|
2
|
+
require "active_record/connection_adapters/statement_pool"
|
|
3
|
+
require "active_record/connection_adapters/mysql2_adapter"
|
|
4
|
+
require "active_record/connection_adapters/mysql2/database_statements"
|
|
5
|
+
require "active_record/connection_adapters/patch_connection_handling"
|
|
6
|
+
require "active_support/core_ext/string/filters"
|
|
7
|
+
require "departure"
|
|
8
|
+
require "forwardable"
|
|
9
|
+
require "active_record/connection_adapters/departure_adapter_behavior"
|
|
9
10
|
|
|
10
11
|
module ActiveRecord
|
|
11
12
|
module ConnectionAdapters
|
|
12
13
|
class Rails80DepartureAdapter < AbstractMysqlAdapter
|
|
13
14
|
include ActiveRecord::ConnectionAdapters::Mysql2::DatabaseStatements
|
|
15
|
+
|
|
14
16
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) } if defined?(initialize_type_map)
|
|
15
17
|
|
|
16
18
|
class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
|
|
@@ -19,24 +21,11 @@ module ActiveRecord
|
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
class SchemaCreation < ActiveRecord::ConnectionAdapters::MySQL::SchemaCreation
|
|
23
|
-
def visit_DropForeignKey(name) # rubocop:disable Naming/MethodName
|
|
24
|
-
fk_name =
|
|
25
|
-
if name =~ /^__(.+)/
|
|
26
|
-
Regexp.last_match(1)
|
|
27
|
-
else
|
|
28
|
-
"_#{name}"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
"DROP FOREIGN KEY #{fk_name}"
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
24
|
extend Forwardable
|
|
36
25
|
|
|
37
|
-
include
|
|
26
|
+
include DepartureAdapterBehavior::SchemaStatements
|
|
38
27
|
|
|
39
|
-
ADAPTER_NAME =
|
|
28
|
+
ADAPTER_NAME = "Percona".freeze
|
|
40
29
|
|
|
41
30
|
def self.new_client(config)
|
|
42
31
|
connection_details = Departure::ConnectionDetails.new(config)
|
|
@@ -47,7 +36,7 @@ module ActiveRecord
|
|
|
47
36
|
percona_logger = Departure::LoggerFactory.build(sanitizers: sanitizers, verbose: verbose)
|
|
48
37
|
cli_generator = Departure::CliGenerator.new(connection_details)
|
|
49
38
|
|
|
50
|
-
mysql_adapter = ActiveRecord::ConnectionAdapters::Mysql2Adapter.new(config.merge(adapter:
|
|
39
|
+
mysql_adapter = ActiveRecord::ConnectionAdapters::Mysql2Adapter.new(config.merge(adapter: "mysql2"))
|
|
51
40
|
|
|
52
41
|
Departure::Runner.new(
|
|
53
42
|
percona_logger,
|
|
@@ -62,7 +51,7 @@ module ActiveRecord
|
|
|
62
51
|
@config[:flags] ||= 0
|
|
63
52
|
|
|
64
53
|
if @config[:flags].is_a? Array
|
|
65
|
-
@config[:flags].push
|
|
54
|
+
@config[:flags].push "FOUND_ROWS"
|
|
66
55
|
else
|
|
67
56
|
@config[:flags] |= ::Mysql2::Client::FOUND_ROWS
|
|
68
57
|
end
|
|
@@ -81,18 +70,18 @@ module ActiveRecord
|
|
|
81
70
|
|
|
82
71
|
@raw_connection.affected_rows
|
|
83
72
|
end
|
|
84
|
-
|
|
73
|
+
alias_method :exec_update, :exec_delete
|
|
85
74
|
|
|
86
75
|
def exec_insert(sql, name, binds, pky = nil, sequence_name = nil, returning: nil) # rubocop:disable Lint/UnusedMethodArgument, Metrics/ParameterLists
|
|
87
76
|
execute(to_sql(sql, binds), name)
|
|
88
77
|
end
|
|
89
78
|
|
|
90
|
-
def internal_exec_query(sql, name =
|
|
79
|
+
def internal_exec_query(sql, name = "SQL", _binds = [], **_kwargs) # :nodoc:
|
|
91
80
|
result = execute(sql, name)
|
|
92
81
|
fields = result.fields if defined?(result.fields)
|
|
93
82
|
ActiveRecord::Result.new(fields || [], result.to_a)
|
|
94
83
|
end
|
|
95
|
-
|
|
84
|
+
alias_method :exec_query, :internal_exec_query
|
|
96
85
|
|
|
97
86
|
# Executes a SELECT query and returns an array of rows. Each row is an
|
|
98
87
|
# array of field values.
|
|
@@ -116,35 +105,6 @@ module ActiveRecord
|
|
|
116
105
|
Column.new(...)
|
|
117
106
|
end
|
|
118
107
|
|
|
119
|
-
# Adds a new index to the table
|
|
120
|
-
#
|
|
121
|
-
# @param table_name [String, Symbol]
|
|
122
|
-
# @param column_name [String, Symbol]
|
|
123
|
-
# @param options [Hash] optional
|
|
124
|
-
def add_index(table_name, column_name, options = {})
|
|
125
|
-
index_definition, = add_index_options(table_name, column_name, **options)
|
|
126
|
-
execute <<-SQL.squish
|
|
127
|
-
ALTER TABLE #{quote_table_name(index_definition.table)}
|
|
128
|
-
ADD #{schema_creation.accept(index_definition)}
|
|
129
|
-
SQL
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Remove the given index from the table.
|
|
133
|
-
#
|
|
134
|
-
# @param table_name [String, Symbol]
|
|
135
|
-
# @param options [Hash] optional
|
|
136
|
-
def remove_index(table_name, column_name = nil, **options)
|
|
137
|
-
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
|
138
|
-
|
|
139
|
-
index_name = index_name_for_remove(table_name, column_name, options)
|
|
140
|
-
|
|
141
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP INDEX #{quote_column_name(index_name)}"
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def schema_creation
|
|
145
|
-
SchemaCreation.new(self)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
108
|
def change_table(table_name, _options = {})
|
|
149
109
|
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
|
150
110
|
yield update_table_definition(table_name, recorder)
|
|
@@ -183,21 +143,21 @@ module ActiveRecord
|
|
|
183
143
|
|
|
184
144
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/ParameterLists
|
|
185
145
|
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false,
|
|
186
|
-
|
|
146
|
+
materialize_transactions: true, batch: false)
|
|
187
147
|
type_casted_binds = type_casted_binds(binds)
|
|
188
148
|
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
189
149
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
190
150
|
perform_query(conn, sql, binds, type_casted_binds, prepare: prepare,
|
|
191
|
-
|
|
151
|
+
notification_payload: notification_payload, batch: batch)
|
|
192
152
|
end
|
|
193
153
|
end
|
|
194
154
|
end
|
|
195
155
|
|
|
196
156
|
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
|
197
157
|
reset_multi_statement = if batch && !multi_statements_enabled?
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
158
|
+
raw_connection.set_server_option(::Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
|
159
|
+
true
|
|
160
|
+
end
|
|
201
161
|
|
|
202
162
|
# Make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
|
203
163
|
# made since we established the connection
|
|
@@ -216,10 +176,10 @@ module ActiveRecord
|
|
|
216
176
|
# THIS IS THE CORE CHANGES 1 related to size
|
|
217
177
|
# We will sometimes have a process exit code instead of a result from executing
|
|
218
178
|
@affected_rows_before_warnings = if result.is_a? Process::Status
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
179
|
+
result.try(:size) || 0
|
|
180
|
+
else
|
|
181
|
+
result.try(:size) || raw_connection.affected_rows
|
|
182
|
+
end
|
|
223
183
|
end
|
|
224
184
|
elsif prepare
|
|
225
185
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "active_record/connection_adapters/abstract_mysql_adapter"
|
|
2
|
+
require "active_record/connection_adapters/trilogy_adapter"
|
|
3
|
+
require "active_record/connection_adapters/patch_connection_handling"
|
|
4
|
+
require "departure"
|
|
5
|
+
require "active_record/connection_adapters/departure_adapter_behavior"
|
|
6
|
+
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
module ConnectionAdapters
|
|
9
|
+
class Rails80TrilogyAdapter < ActiveRecord::ConnectionAdapters::TrilogyAdapter
|
|
10
|
+
class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
|
|
11
|
+
def adapter
|
|
12
|
+
Rails80TrilogyAdapter
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
ADAPTER_NAME = "Percona".freeze
|
|
17
|
+
|
|
18
|
+
include DepartureAdapterBehavior::Rails8
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "active_record/connection_adapters/abstract_mysql_adapter"
|
|
2
|
+
require "active_record/connection_adapters/mysql2_adapter"
|
|
3
|
+
require "active_record/connection_adapters/patch_connection_handling"
|
|
4
|
+
require "departure"
|
|
5
|
+
require "active_record/connection_adapters/departure_adapter_behavior"
|
|
6
|
+
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
module ConnectionAdapters
|
|
9
|
+
class Rails81Mysql2Adapter < ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
|
10
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) } if defined?(initialize_type_map)
|
|
11
|
+
|
|
12
|
+
class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
|
|
13
|
+
def adapter
|
|
14
|
+
Rails81Mysql2Adapter
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ADAPTER_NAME = "Percona".freeze
|
|
19
|
+
|
|
20
|
+
include DepartureAdapterBehavior::Rails8
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "active_record/connection_adapters/abstract_mysql_adapter"
|
|
2
|
+
require "active_record/connection_adapters/trilogy_adapter"
|
|
3
|
+
require "active_record/connection_adapters/patch_connection_handling"
|
|
4
|
+
require "departure"
|
|
5
|
+
require "active_record/connection_adapters/departure_adapter_behavior"
|
|
6
|
+
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
module ConnectionAdapters
|
|
9
|
+
class Rails81TrilogyAdapter < ActiveRecord::ConnectionAdapters::TrilogyAdapter
|
|
10
|
+
class Column < ActiveRecord::ConnectionAdapters::MySQL::Column
|
|
11
|
+
def adapter
|
|
12
|
+
Rails81TrilogyAdapter
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
ADAPTER_NAME = "Percona".freeze
|
|
17
|
+
|
|
18
|
+
include DepartureAdapterBehavior::Rails8
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
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 [^\s]
|
|
7
|
+
ALTER_TABLE_REGEX = /\AALTER TABLE [^\s]*\n* /
|
|
8
8
|
|
|
9
9
|
attr_reader :table_name
|
|
10
10
|
|
|
@@ -21,8 +21,8 @@ module Departure
|
|
|
21
21
|
#
|
|
22
22
|
# Removes the grave marks, if they are there, so we can get the table_name
|
|
23
23
|
@table_name = String(match)
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
.split(" ")[2]
|
|
25
|
+
.delete("`")
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Returns the '--alter' pt-online-schema-change argument as a string. See
|
|
@@ -40,9 +40,9 @@ module Departure
|
|
|
40
40
|
# @return [String]
|
|
41
41
|
def parsed_statement
|
|
42
42
|
@parsed_statement ||= statement
|
|
43
|
-
.gsub(ALTER_TABLE_REGEX,
|
|
44
|
-
.gsub(
|
|
45
|
-
.gsub(
|
|
43
|
+
.gsub(ALTER_TABLE_REGEX, "")
|
|
44
|
+
.gsub("`", '\\\`')
|
|
45
|
+
.gsub("\\n", "")
|
|
46
46
|
.gsub('"', '\\\"')
|
|
47
47
|
end
|
|
48
48
|
end
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
1
|
+
require "departure/dsn"
|
|
2
|
+
require "departure/option"
|
|
3
|
+
require "departure/alter_argument"
|
|
4
|
+
require "departure/connection_details"
|
|
5
|
+
require "departure/user_options"
|
|
6
6
|
|
|
7
7
|
module Departure
|
|
8
8
|
# Generates the equivalent Percona's pt-online-schema-change command to the
|
|
9
9
|
# given SQL statement
|
|
10
10
|
#
|
|
11
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
|
|
12
|
+
# www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html#cmdoption-pt-online-schema-change--[no]check-alter
|
|
13
13
|
#
|
|
14
14
|
class CliGenerator
|
|
15
|
-
COMMAND_NAME =
|
|
15
|
+
COMMAND_NAME = "pt-online-schema-change".freeze
|
|
16
16
|
DEFAULT_OPTIONS = Set.new(
|
|
17
17
|
[
|
|
18
|
-
Option.new(
|
|
19
|
-
Option.new(
|
|
20
|
-
Option.new(
|
|
21
|
-
Option.new(
|
|
18
|
+
Option.new("execute"),
|
|
19
|
+
Option.new("statistics"),
|
|
20
|
+
Option.new("alter-foreign-keys-method", "auto"),
|
|
21
|
+
Option.new("no-check-alter")
|
|
22
22
|
]
|
|
23
23
|
).freeze
|
|
24
24
|
|
|
@@ -78,7 +78,7 @@ module Departure
|
|
|
78
78
|
env_variable_options = UserOptions.new
|
|
79
79
|
global_configuration_options = UserOptions.new(Departure.configuration.global_percona_args)
|
|
80
80
|
options = env_variable_options.merge(global_configuration_options).merge(DEFAULT_OPTIONS)
|
|
81
|
-
options.to_a.join(
|
|
81
|
+
options.to_a.join(" ")
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
data/lib/departure/command.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module Departure
|
|
2
2
|
class Configuration
|
|
3
3
|
attr_accessor :tmp_path, :global_percona_args, :enabled_by_default, :redirect_stderr,
|
|
4
|
-
|
|
4
|
+
:disable_rails_advisory_lock_patch
|
|
5
5
|
|
|
6
6
|
def initialize
|
|
7
|
-
@tmp_path =
|
|
8
|
-
@error_log_filename =
|
|
7
|
+
@tmp_path = ".".freeze
|
|
8
|
+
@error_log_filename = "departure_error.log".freeze
|
|
9
9
|
@global_percona_args = nil
|
|
10
10
|
@enabled_by_default = true
|
|
11
11
|
@redirect_stderr = true
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "shellwords"
|
|
2
2
|
module Departure
|
|
3
3
|
# Holds the parameters of the DB connection and formats them to string
|
|
4
4
|
class ConnectionDetails
|
|
@@ -26,7 +26,7 @@ module Departure
|
|
|
26
26
|
#
|
|
27
27
|
# @return [String]
|
|
28
28
|
def database
|
|
29
|
-
ENV.fetch(
|
|
29
|
+
ENV.fetch("PERCONA_DB_NAME", connection_data[:database])
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
# Returns the password fragment of the details string if a password is passed
|
|
@@ -36,7 +36,7 @@ module Departure
|
|
|
36
36
|
if password.present?
|
|
37
37
|
%(--password #{Shellwords.escape(password)} )
|
|
38
38
|
else
|
|
39
|
-
|
|
39
|
+
""
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -77,7 +77,7 @@ module Departure
|
|
|
77
77
|
#
|
|
78
78
|
# @return [String]
|
|
79
79
|
def host
|
|
80
|
-
ENV.fetch(
|
|
80
|
+
ENV.fetch("PERCONA_DB_HOST", connection_data[:host]) || "localhost"
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
# Returns the database user. If PERCONA_DB_USER is passed its value will be
|
|
@@ -85,7 +85,7 @@ module Departure
|
|
|
85
85
|
#
|
|
86
86
|
# @return [String]
|
|
87
87
|
def user
|
|
88
|
-
ENV.fetch(
|
|
88
|
+
ENV.fetch("PERCONA_DB_USER", connection_data[:username])
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# Returns the database user's password. If PERCONA_DB_PASSWORD is passed its
|
|
@@ -93,7 +93,7 @@ module Departure
|
|
|
93
93
|
#
|
|
94
94
|
# @return [String]
|
|
95
95
|
def password
|
|
96
|
-
ENV.fetch(
|
|
96
|
+
ENV.fetch("PERCONA_DB_PASSWORD", connection_data[:password])
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# Returns the database socket path. If PERCONA_DB_SOCKET is passed its value
|
|
@@ -101,7 +101,7 @@ module Departure
|
|
|
101
101
|
#
|
|
102
102
|
# @return [String]
|
|
103
103
|
def socket
|
|
104
|
-
ENV.fetch(
|
|
104
|
+
ENV.fetch("PERCONA_DB_SOCKET", connection_data[:socket])
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
# Returns the database's port.
|
data/lib/departure/db_client.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "open3"
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "forwardable"
|
|
4
4
|
|
|
5
5
|
module Departure
|
|
6
6
|
# It executes pt-online-schema-change commands in a new process and gets its
|
|
@@ -40,9 +40,9 @@ module Departure
|
|
|
40
40
|
command_line = @cli_generator.parse_statement(raw_sql_string)
|
|
41
41
|
|
|
42
42
|
Command.new(command_line,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
Departure.configuration.error_log_path,
|
|
44
|
+
@logger,
|
|
45
|
+
Departure.configuration.redirect_stderr).run
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def alter_statement?(raw_sql_string)
|
data/lib/departure/dsn.rb
CHANGED
|
@@ -9,13 +9,13 @@ module Departure
|
|
|
9
9
|
def initialize(database, table_name)
|
|
10
10
|
@database = database
|
|
11
11
|
@table_name = table_name
|
|
12
|
-
@suffix = ENV.fetch(
|
|
12
|
+
@suffix = ENV.fetch("PERCONA_DSN_SUFFIX", nil)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# Returns the pt-online-schema-change DSN string. See
|
|
16
16
|
# https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html#dsn-options
|
|
17
17
|
def to_s
|
|
18
|
-
"D=#{database},t=#{table_name}#{
|
|
18
|
+
"D=#{database},t=#{table_name}#{"," + suffix unless suffix.nil?}"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
private
|
data/lib/departure/errors.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Departure
|
|
|
5
5
|
# status.
|
|
6
6
|
class NoStatusError < Error
|
|
7
7
|
def message
|
|
8
|
-
|
|
8
|
+
"Status could not be retrieved".freeze
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
@@ -29,7 +29,7 @@ module Departure
|
|
|
29
29
|
|
|
30
30
|
class CommandNotFoundError < Error
|
|
31
31
|
def message
|
|
32
|
-
|
|
32
|
+
"Please install pt-online-schema-change. Check: https://www.percona.com/doc/percona-toolkit for further details"
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
data/lib/departure/logger.rb
CHANGED
|
@@ -14,13 +14,13 @@ module Departure
|
|
|
14
14
|
# @param message [String]
|
|
15
15
|
# @param subitem [Boolean] whether to show message as a nested log item
|
|
16
16
|
def say(message, subitem = false)
|
|
17
|
-
write "#{subitem ?
|
|
17
|
+
write "#{subitem ? " ->" : "--"} #{message}"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Outputs the text through the stdout adding a new line at the end
|
|
21
21
|
#
|
|
22
22
|
# @param text [String]
|
|
23
|
-
def write(text =
|
|
23
|
+
def write(text = "")
|
|
24
24
|
puts(sanitize(text))
|
|
25
25
|
end
|
|
26
26
|
|
data/lib/departure/migration.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Departure
|
|
|
8
8
|
module Migration
|
|
9
9
|
extend ActiveSupport::Concern
|
|
10
10
|
|
|
11
|
+
DEPARTURE_ADAPTERS = %w[percona percona_trilogy].freeze
|
|
12
|
+
|
|
11
13
|
included do
|
|
12
14
|
# Holds the name of the adapter that was configured by the app.
|
|
13
15
|
mattr_accessor :original_adapter
|
|
@@ -53,11 +55,13 @@ module Departure
|
|
|
53
55
|
# Migrate with or without Departure based on uses_departure class
|
|
54
56
|
# attribute.
|
|
55
57
|
def migrate(direction)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
with_restored_connection_specification_name do
|
|
59
|
+
if uses_departure?
|
|
60
|
+
departure_migrate(direction)
|
|
61
|
+
else
|
|
62
|
+
reconnect_without_percona
|
|
63
|
+
active_record_migrate(direction)
|
|
64
|
+
end
|
|
61
65
|
end
|
|
62
66
|
end
|
|
63
67
|
|
|
@@ -73,15 +77,29 @@ module Departure
|
|
|
73
77
|
# Make all connections in the connection pool to use PerconaAdapter
|
|
74
78
|
# instead of the current adapter.
|
|
75
79
|
def reconnect_with_percona
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
config = connection_config
|
|
81
|
+
return if departure_adapter_config?(config)
|
|
82
|
+
|
|
83
|
+
departure_adapter = Departure::RailsAdapter.for_current(db_connection_adapter: config[:adapter])
|
|
84
|
+
Departure::ConnectionBase.establish_connection(
|
|
85
|
+
config.merge(
|
|
86
|
+
adapter: departure_adapter.departure_adapter_name,
|
|
87
|
+
departure_original_adapter: config[:adapter]
|
|
88
|
+
)
|
|
89
|
+
)
|
|
78
90
|
end
|
|
79
91
|
|
|
80
92
|
# Reconnect without percona adapter when Departure is disabled but was
|
|
81
93
|
# enabled in a previous migration.
|
|
82
94
|
def reconnect_without_percona
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
config = connection_config
|
|
96
|
+
return unless departure_adapter_config?(config)
|
|
97
|
+
|
|
98
|
+
Departure::OriginalAdapterConnection.establish_connection(
|
|
99
|
+
config
|
|
100
|
+
.except(:departure_original_adapter)
|
|
101
|
+
.merge(adapter: config[:departure_original_adapter] || original_adapter)
|
|
102
|
+
)
|
|
85
103
|
end
|
|
86
104
|
|
|
87
105
|
private
|
|
@@ -89,12 +107,24 @@ module Departure
|
|
|
89
107
|
# Capture the type of the adapter configured by the app if not already set.
|
|
90
108
|
def connection_config
|
|
91
109
|
configuration_hash.tap do |config|
|
|
92
|
-
self.class.original_adapter ||= config[:adapter]
|
|
110
|
+
self.class.original_adapter ||= config[:departure_original_adapter] || config[:adapter]
|
|
93
111
|
end
|
|
94
112
|
end
|
|
95
113
|
|
|
114
|
+
private def departure_adapter_config?(config)
|
|
115
|
+
DEPARTURE_ADAPTERS.include?(config[:adapter])
|
|
116
|
+
end
|
|
117
|
+
|
|
96
118
|
private def configuration_hash
|
|
97
119
|
ActiveRecord::Base.connection_db_config.configuration_hash
|
|
98
120
|
end
|
|
121
|
+
|
|
122
|
+
private def with_restored_connection_specification_name
|
|
123
|
+
connection_specification_name = ActiveRecord::Base.connection_specification_name
|
|
124
|
+
|
|
125
|
+
yield
|
|
126
|
+
ensure
|
|
127
|
+
ActiveRecord::Base.connection_specification_name = connection_specification_name
|
|
128
|
+
end
|
|
99
129
|
end
|
|
100
130
|
end
|
data/lib/departure/option.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Departure
|
|
|
27
27
|
def ==(other)
|
|
28
28
|
name == other.name
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
alias_method :eql?, :==
|
|
31
31
|
|
|
32
32
|
# Returns the option's hash
|
|
33
33
|
#
|
|
@@ -50,7 +50,7 @@ module Departure
|
|
|
50
50
|
#
|
|
51
51
|
# @return [String]
|
|
52
52
|
def normalize_option(name)
|
|
53
|
-
if name.start_with?(
|
|
53
|
+
if name.start_with?("-")
|
|
54
54
|
name
|
|
55
55
|
elsif name.length == 1
|
|
56
56
|
"-#{name}"
|
|
@@ -64,8 +64,8 @@ module Departure
|
|
|
64
64
|
# @return [String]
|
|
65
65
|
def value_as_string
|
|
66
66
|
if value.nil?
|
|
67
|
-
|
|
68
|
-
elsif value.include?(
|
|
67
|
+
""
|
|
68
|
+
elsif value.include?("=")
|
|
69
69
|
" #{value}"
|
|
70
70
|
else
|
|
71
71
|
"=#{value}"
|