percona_migrator 0.1.0.rc.1
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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +50 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +90 -0
- data/Rakefile +17 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/config.yml +3 -0
- data/configuration.rb +15 -0
- data/lib/active_record/connection_adapters/percona_adapter.rb +153 -0
- data/lib/lhm/adapter.rb +109 -0
- data/lib/lhm/column_with_sql.rb +90 -0
- data/lib/lhm/column_with_type.rb +31 -0
- data/lib/lhm.rb +25 -0
- data/lib/percona_migrator/alter_argument.rb +33 -0
- data/lib/percona_migrator/cli_generator.rb +117 -0
- data/lib/percona_migrator/railtie.rb +44 -0
- data/lib/percona_migrator/runner.rb +88 -0
- data/lib/percona_migrator/version.rb +3 -0
- data/lib/percona_migrator.rb +11 -0
- data/percona_migrator.gemspec +30 -0
- data/test_database.rb +25 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 54dcba8208db6d98ee85f3124a8b4d725617e80d
|
4
|
+
data.tar.gz: 86b55bab745a0952886b09aa32323f78db9e8d9f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 695c5ed29fa887f1f8df33b66ab86a8d63c6fde601e17ff91440f237ec05d62c6e4645128fa144a29dd412b1dbcfa9ef56356e5caa04833d2a88a2ff8ce20200
|
7
|
+
data.tar.gz: 36d59e8106ceb679b867e90f2d0b1598f17fa74a4eb79d33f73b0c13311ac82a7dd20c990d9a8a711969cec61e1b1de0bbe393205a6a89860569d3248f0515e7
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.2
|
4
|
+
before_install:
|
5
|
+
- gpg --keyserver hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A && gpg -a --export CD2EFD2A | sudo apt-key add -
|
6
|
+
- echo "deb http://repo.percona.com/apt `lsb_release -cs` main" | sudo tee -a /etc/apt/sources.list
|
7
|
+
- sudo apt-get update -qq
|
8
|
+
- sudo apt-get install percona-toolkit
|
9
|
+
- gem update bundler
|
10
|
+
- bin/setup
|
11
|
+
|
12
|
+
branches:
|
13
|
+
only:
|
14
|
+
- master
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
Please follow the format in [Keep a Changelog](http://keepachangelog.com/)
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
- Initial gem version
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at accounts@redbooth.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
|
45
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
46
|
+
version 1.3.0, available at
|
47
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
48
|
+
|
49
|
+
[homepage]: http://contributor-covenant.org
|
50
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Pau Pérez
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# PerconaMigrator [](https://travis-ci.org/redbooth/percona_migrator)
|
2
|
+
|
3
|
+
Percona Migrator is a tool for running online and non-blocking
|
4
|
+
DDL `ActiveRecord::Migrations` using `pt-online-schema-change` command-line tool of
|
5
|
+
[Percona
|
6
|
+
Toolkit](https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html)
|
7
|
+
which supports foreign key constraints.
|
8
|
+
|
9
|
+
It adds a `db:percona_migrate:up` runs your migration using the
|
10
|
+
`pt-online-schema-change` command. It will apply exactly the same changes as
|
11
|
+
if you run it with `db:migrate:up` avoiding deadlocks and without the need to
|
12
|
+
change how you write regular rails migrations.
|
13
|
+
|
14
|
+
It also disables `rake db:migrate:up` for the ddl migrations on envs with
|
15
|
+
PERCONA_TOOLKIT var set to ensure all these migrations use Percona in production.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Percona Migrator relies on `pt-online-schema-change` from [Percona
|
20
|
+
Toolkit](https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html)
|
21
|
+
|
22
|
+
For mac, you can install it with homebrew typing `brew install percona-toolkit`. For
|
23
|
+
linux machines check out the [Percona Toolkit download
|
24
|
+
page](https://www.percona.com/downloads/percona-toolkit/) to find the package
|
25
|
+
that fits your distribution.
|
26
|
+
|
27
|
+
You can also get it from [Percona's apt repository](https://www.percona.com/doc/percona-xtradb-cluster/5.5/installation/apt_repo.html)
|
28
|
+
|
29
|
+
Once installed, add this line to your application's Gemfile:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
gem 'percona_migrator'
|
33
|
+
```
|
34
|
+
|
35
|
+
And then execute:
|
36
|
+
|
37
|
+
$ bundle
|
38
|
+
|
39
|
+
Or install it yourself as:
|
40
|
+
|
41
|
+
$ gem install percona_migrator
|
42
|
+
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
Percona Migrator is meant to be used only on production or production-like
|
46
|
+
environments. To that end, it will only run if the `PERCONA_TOOLKIT`
|
47
|
+
environment variable is present.
|
48
|
+
|
49
|
+
From that same environment where you added the variable, execute the following:
|
50
|
+
|
51
|
+
1. `bundle exec rake db:migrate:status` to find out your migration's version
|
52
|
+
number
|
53
|
+
2. `rake db:percona_migrate:up VERSION=<version>`. This will run the migration
|
54
|
+
and mark it as up. Otherwise, if the migration fails, it will still be listed as down
|
55
|
+
|
56
|
+
You can also mark the migration as run manually, by executing `bundle exec rake
|
57
|
+
db:migrate:mark_as_up VERSION=<version>`. Likewise, there's a `bundle exec rake
|
58
|
+
db:migrate:mark_as_down VERSION=<version>` that may be of help.
|
59
|
+
|
60
|
+
## Development
|
61
|
+
|
62
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
63
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
64
|
+
prompt that will allow you to experiment.
|
65
|
+
|
66
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
67
|
+
release a new version, update the version number in `version.rb`, and then run
|
68
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
69
|
+
git commits and tags, and push the `.gem` file to
|
70
|
+
[rubygems.org](https://rubygems.org).
|
71
|
+
|
72
|
+
## Contributing
|
73
|
+
|
74
|
+
Bug reports and pull requests are welcome on GitHub at
|
75
|
+
https://github.com/redbooth/percona_migrator.
|
76
|
+
|
77
|
+
Please note that this project is released with a Contributor Code of Conduct. By
|
78
|
+
participating in this project you agree to abide by its terms.
|
79
|
+
|
80
|
+
Check the code of conduct [here](CODE_OF_CONDUCT.md)
|
81
|
+
|
82
|
+
## Changelog
|
83
|
+
|
84
|
+
You can consult the changelog [here](CHANGELOG.md)
|
85
|
+
|
86
|
+
## License
|
87
|
+
|
88
|
+
The gem is available as open source under the terms of the [MIT
|
89
|
+
License](http://opensource.org/licenses/MIT).
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
require './configuration'
|
5
|
+
require './test_database'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
task :default => :spec
|
10
|
+
|
11
|
+
namespace :db do
|
12
|
+
desc 'Create the test database'
|
13
|
+
task :create do
|
14
|
+
config = Configuration.new
|
15
|
+
TestDatabase.new(config).create_test_database
|
16
|
+
end
|
17
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "percona_migrator"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/config.yml
ADDED
data/configuration.rb
ADDED
@@ -0,0 +1,153 @@
|
|
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 'percona_migrator'
|
5
|
+
require 'forwardable'
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
class Base
|
9
|
+
# Establishes a connection to the database that's used by all Active
|
10
|
+
# Record objects.
|
11
|
+
def self.percona_connection(config)
|
12
|
+
connection = mysql2_connection(config)
|
13
|
+
client = connection.raw_connection
|
14
|
+
|
15
|
+
# TODO: use AR's logger. It must pass a logger instance around, at least
|
16
|
+
# the one the migration uses
|
17
|
+
logger = config[:logger] || $stdout
|
18
|
+
|
19
|
+
config.merge!(
|
20
|
+
logger: logger,
|
21
|
+
runner: PerconaMigrator::Runner.new(logger),
|
22
|
+
cli_generator: PerconaMigrator::CliGenerator.new(config)
|
23
|
+
)
|
24
|
+
|
25
|
+
connection_options = { mysql_adapter: connection }
|
26
|
+
|
27
|
+
ConnectionAdapters::PerconaMigratorAdapter.new(
|
28
|
+
client,
|
29
|
+
logger,
|
30
|
+
connection_options,
|
31
|
+
config
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ConnectionAdapters
|
37
|
+
# It doesn't implement #create_table as this statement is harmless and
|
38
|
+
# pretty fast. No need to do it with Percona
|
39
|
+
class PerconaMigratorAdapter < AbstractMysqlAdapter
|
40
|
+
|
41
|
+
class Column < AbstractMysqlAdapter::Column
|
42
|
+
def adapter
|
43
|
+
PerconaMigratorAdapter
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
extend Forwardable
|
48
|
+
|
49
|
+
ADAPTER_NAME = 'Percona'.freeze
|
50
|
+
|
51
|
+
def_delegators :mysql_adapter, :tables, :select_values, :exec_delete,
|
52
|
+
:exec_insert, :exec_query, :last_inserted_id, :select
|
53
|
+
|
54
|
+
def initialize(connection, logger, connection_options, config)
|
55
|
+
super
|
56
|
+
@mysql_adapter = connection_options[:mysql_adapter]
|
57
|
+
@logger = logger
|
58
|
+
@runner = config[:runner]
|
59
|
+
@cli_generator = config[:cli_generator]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true, as this adapter supports migrations
|
63
|
+
def supports_migrations?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Delegates #each_hash to the mysql adapter
|
68
|
+
#
|
69
|
+
# @param result [Mysql2::Result]
|
70
|
+
def each_hash(result)
|
71
|
+
if block_given?
|
72
|
+
mysql_adapter.each_hash(result, &Proc.new)
|
73
|
+
else
|
74
|
+
mysql_adapter.each_hash(result)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def new_column(field, default, type, null, collation)
|
79
|
+
Column.new(field, default, type, null, collation)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds a new column to the named table
|
83
|
+
#
|
84
|
+
# @param table_name [String, Symbol]
|
85
|
+
# @param column_name [String, Symbol]
|
86
|
+
# @param type [Symbol]
|
87
|
+
# @param options [Hash] optional
|
88
|
+
def add_column(table_name, column_name, type, options = {})
|
89
|
+
super
|
90
|
+
command = cli_generator.generate(table_name, @sql)
|
91
|
+
runner.execute(command)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Removes the column(s) from the table definition
|
95
|
+
#
|
96
|
+
# @param table_name [String, Symbol]
|
97
|
+
# @param column_names [String, Symbol, Array<String>, Array<Symbol>]
|
98
|
+
def remove_column(table_name, *column_names)
|
99
|
+
super
|
100
|
+
command = cli_generator.generate(table_name, @sql)
|
101
|
+
runner.execute(command)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Adds a new index to the table
|
105
|
+
#
|
106
|
+
# @param table_name [String, Symbol]
|
107
|
+
# @param column_name [String, Symbol]
|
108
|
+
# @param options [Hash] optional
|
109
|
+
def add_index(table_name, column_name, options = {})
|
110
|
+
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
|
111
|
+
execute "ADD #{index_type} INDEX #{quote_column_name(index_name)} (#{index_columns})#{index_options}"
|
112
|
+
|
113
|
+
command = cli_generator.generate(table_name, @sql)
|
114
|
+
runner.execute(command)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Remove the given index from the table.
|
118
|
+
#
|
119
|
+
# @param table_name [String, Symbol]
|
120
|
+
# @param options [Hash] optional
|
121
|
+
def remove_index(table_name, options = {})
|
122
|
+
index_name = index_name_for_remove(table_name, options)
|
123
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
124
|
+
|
125
|
+
command = cli_generator.generate(table_name, @sql)
|
126
|
+
runner.execute(command)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Records the SQL statement to be executed. This is used to then delegate
|
130
|
+
# the execution to Percona's pt-online-schema-change.
|
131
|
+
#
|
132
|
+
# @param sql [String]
|
133
|
+
# @param _name [String] optional
|
134
|
+
def execute(sql, _name = nil)
|
135
|
+
@sql = sql
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
# This abstract method leaves up to the connection adapter freeing the
|
140
|
+
# result, if it needs to. Check out: https://github.com/rails/rails/blob/330c6af05c8b188eb072afa56c07d5fe15767c3c/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L247
|
141
|
+
#
|
142
|
+
# @param sql [String]
|
143
|
+
# @param name [String] optional
|
144
|
+
def execute_and_free(sql, name = nil)
|
145
|
+
yield mysql_adapter.execute(sql, name)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
attr_reader :mysql_adapter, :logger, :runner, :cli_generator
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/lhm/adapter.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'lhm/column_with_sql'
|
2
|
+
require 'lhm/column_with_type'
|
3
|
+
|
4
|
+
module Lhm
|
5
|
+
|
6
|
+
# Translates Lhm DSL to ActiveRecord's one, so Lhm migrations will now go
|
7
|
+
# through Percona as well, without any modification on the migration's
|
8
|
+
# code
|
9
|
+
class Adapter
|
10
|
+
|
11
|
+
# Constructor
|
12
|
+
#
|
13
|
+
# @param migration [ActiveRecord::Migtration]
|
14
|
+
# @param table_name [String, Symbol]
|
15
|
+
def initialize(migration, table_name)
|
16
|
+
@migration = migration
|
17
|
+
@table_name = table_name
|
18
|
+
end
|
19
|
+
|
20
|
+
# Adds the specified column through ActiveRecord
|
21
|
+
#
|
22
|
+
# @param column_name [String, Symbol]
|
23
|
+
# @param definition [String, Symbol]
|
24
|
+
def add_column(column_name, definition)
|
25
|
+
attributes = column_attributes(column_name, definition)
|
26
|
+
migration.add_column(*attributes)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Removes the specified column through ActiveRecord
|
30
|
+
#
|
31
|
+
# @param column_name [String, Symbol]
|
32
|
+
def remove_column(column_name)
|
33
|
+
migration.remove_column(table_name, column_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Adds an index in the specified columns through ActiveRecord. Note you
|
37
|
+
# can provide a name as well
|
38
|
+
#
|
39
|
+
# @param columns [Array<String>, Array<Symbol>, String, Symbol]
|
40
|
+
# @param index_name [String]
|
41
|
+
def add_index(columns, index_name = nil)
|
42
|
+
options = { name: index_name } if index_name
|
43
|
+
migration.add_index(table_name, columns, options || {})
|
44
|
+
end
|
45
|
+
|
46
|
+
# Removes the index in the given columns or by its name
|
47
|
+
#
|
48
|
+
# @param columns [Array<String>, Array<Symbol>, String, Symbol]
|
49
|
+
# @param index_name [String]
|
50
|
+
def remove_index(columns, index_name = nil)
|
51
|
+
options = if index_name
|
52
|
+
{ name: index_name }
|
53
|
+
else
|
54
|
+
{ column: columns }
|
55
|
+
end
|
56
|
+
migration.remove_index(table_name, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Change the column to use the provided definition, through ActiveRecord
|
60
|
+
#
|
61
|
+
# @param column_name [String, Symbol]
|
62
|
+
# @param definition [String, Symbol]
|
63
|
+
def change_column(column_name, definition)
|
64
|
+
attributes = column_attributes(column_name, definition)
|
65
|
+
migration.change_column(*attributes)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Renames the old_name column to new_name by using ActiveRecord
|
69
|
+
#
|
70
|
+
# @param old_name [String, Symbol]
|
71
|
+
# @param new_name [String, Symbol]
|
72
|
+
def rename_column(old_name, new_name)
|
73
|
+
migration.rename_column(table_name, old_name, new_name)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Adds a unique index on the given columns, with the provided name if passed
|
77
|
+
#
|
78
|
+
# @param columns [Array<String>, Array<Symbol>, String, Symbol]
|
79
|
+
# @param index_name [String]
|
80
|
+
def add_unique_index(columns, index_name = nil)
|
81
|
+
options = { unique: true }
|
82
|
+
options.merge!(name: index_name) if index_name
|
83
|
+
|
84
|
+
migration.add_index(table_name, columns, options)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
attr_reader :migration, :table_name
|
90
|
+
|
91
|
+
# Returns the instance of ActiveRecord column with the given name and
|
92
|
+
# definition
|
93
|
+
#
|
94
|
+
# @param name [String, Symbol]
|
95
|
+
# @param definition [String]
|
96
|
+
def column(name, definition)
|
97
|
+
@column ||= if definition.is_a?(Symbol)
|
98
|
+
ColumnWithType.new(name, definition)
|
99
|
+
else
|
100
|
+
ColumnWithSql.new(name, definition)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def column_attributes(name, definition)
|
105
|
+
attributes = column(name, definition).attributes
|
106
|
+
[table_name, name].concat(attributes)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Lhm
|
4
|
+
|
5
|
+
# Abstracts the details of a table column definition when specified with a MySQL
|
6
|
+
# column definition string
|
7
|
+
class ColumnWithSql
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
# Returns the column's class to be used
|
11
|
+
#
|
12
|
+
# @return [Constant]
|
13
|
+
def self.column_factory
|
14
|
+
::ActiveRecord::ConnectionAdapters::PerconaMigratorAdapter::Column
|
15
|
+
end
|
16
|
+
|
17
|
+
# Constructor
|
18
|
+
#
|
19
|
+
# @param name [String, Symbol]
|
20
|
+
# @param definition [String]
|
21
|
+
def initialize(name, definition)
|
22
|
+
@name = name
|
23
|
+
@definition = definition
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the column data as an Array to be used with the splat operator.
|
27
|
+
# See Lhm::Adaper#add_column
|
28
|
+
#
|
29
|
+
# @return [Array]
|
30
|
+
def attributes
|
31
|
+
[type, column_options]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def_delegators :column, :limit, :type, :default, :null
|
37
|
+
|
38
|
+
attr_reader :name, :definition
|
39
|
+
|
40
|
+
# TODO: investigate
|
41
|
+
#
|
42
|
+
# Rails doesn't take into account lenght argument of INT in the
|
43
|
+
# definition, as an integer it will default it to 4 not an integer
|
44
|
+
#
|
45
|
+
# Returns the columns data as a Hash
|
46
|
+
#
|
47
|
+
# @return [Hash]
|
48
|
+
def column_options
|
49
|
+
{ limit: column.limit, default: column.default, null: column.null }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the column instance with the provided data
|
53
|
+
#
|
54
|
+
# @return [column_factory]
|
55
|
+
def column
|
56
|
+
@column ||= self.class.column_factory.new(
|
57
|
+
name,
|
58
|
+
default_value,
|
59
|
+
definition,
|
60
|
+
null_value
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Gets the DEFAULT value the column takes as specified in the
|
65
|
+
# definition, if any
|
66
|
+
#
|
67
|
+
# @return [String, NilClass]
|
68
|
+
def default_value
|
69
|
+
match = if definition =~ /timestamp|datetime/i
|
70
|
+
/default '?(.+[^'])'?/i.match(definition)
|
71
|
+
else
|
72
|
+
/default '?(\w+)'?/i.match(definition)
|
73
|
+
end
|
74
|
+
|
75
|
+
return unless match
|
76
|
+
|
77
|
+
match[1].downcase != 'null' ? match[1] : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# Checks whether the column accepts NULL as specified in the definition
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
def null_value
|
84
|
+
match = /((\w*) NULL)/i.match(definition)
|
85
|
+
return true unless match
|
86
|
+
|
87
|
+
match[2].downcase == 'not' ? false : true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lhm
|
2
|
+
|
3
|
+
# Abstracts the details of a table column definition when specified with a type
|
4
|
+
# as a symbol. This is the regular ActiveRecord's #add_column syntax:
|
5
|
+
#
|
6
|
+
# add_column :tablenames, :field, :string
|
7
|
+
#
|
8
|
+
class ColumnWithType
|
9
|
+
|
10
|
+
# Constructor
|
11
|
+
#
|
12
|
+
# @param name [String, Symbol]
|
13
|
+
# @param definition [Symbol]
|
14
|
+
def initialize(name, definition)
|
15
|
+
@name = name
|
16
|
+
@definition = definition
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the column data as an Array to be used with the splat operator.
|
20
|
+
# See Lhm::Adaper#add_column
|
21
|
+
#
|
22
|
+
# @return [Array]
|
23
|
+
def attributes
|
24
|
+
[definition]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :definition
|
30
|
+
end
|
31
|
+
end
|
data/lib/lhm.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lhm/adapter'
|
2
|
+
|
3
|
+
# Defines the same global namespace as LHM's gem does to mimic its API
|
4
|
+
# while providing a different behaviour. We delegate all LHM's methods to
|
5
|
+
# ActiveRecord so that you don't need to modify your old LHM migrations
|
6
|
+
module Lhm
|
7
|
+
|
8
|
+
# Yields an adapter instance so that Lhm migration Dsl methods get
|
9
|
+
# delegated to ActiveRecord::Migration ones instead
|
10
|
+
#
|
11
|
+
# @param table_name [String]
|
12
|
+
# @param _options [Hash]
|
13
|
+
# @param block [Block]
|
14
|
+
def self.change_table(table_name, _options = {}, &block)
|
15
|
+
yield Adapter.new(@migration, table_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Sets the migration to apply the adapter to
|
19
|
+
#
|
20
|
+
# @param migration [ActiveRecord::Migration]
|
21
|
+
def self.migration=(migration)
|
22
|
+
@migration = migration
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module PerconaMigrator
|
2
|
+
|
3
|
+
# Represents the '--alter' argument of Percona's pt-online-schema-change
|
4
|
+
# See https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html
|
5
|
+
class AlterArgument
|
6
|
+
|
7
|
+
# Constructor
|
8
|
+
#
|
9
|
+
# @param statement [String]
|
10
|
+
def initialize(statement)
|
11
|
+
@statement = statement
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the '--alter' pt-online-schema-change argumment as a string. See
|
15
|
+
# https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html
|
16
|
+
def to_s
|
17
|
+
"--alter \"#{parsed_statement}\""
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :statement
|
23
|
+
|
24
|
+
# Removes the 'ALTER TABLE' portion of the SQL statement
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
def parsed_statement
|
28
|
+
@parsed_statement ||= statement
|
29
|
+
.gsub(/ALTER TABLE `(\w+)` /, '')
|
30
|
+
.gsub('`','\\\`')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'percona_migrator/alter_argument'
|
2
|
+
|
3
|
+
module PerconaMigrator
|
4
|
+
|
5
|
+
# Represents the 'DSN' argument of Percona's pt-online-schema-change
|
6
|
+
# See https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html#dsn-options
|
7
|
+
class DSN
|
8
|
+
|
9
|
+
# Constructor
|
10
|
+
#
|
11
|
+
# @param database [String, Symbol]
|
12
|
+
# @param table_name [String, Symbol]
|
13
|
+
def initialize(database, table_name)
|
14
|
+
@database = database
|
15
|
+
@table_name = table_name
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the pt-online-schema-change DSN string. See
|
19
|
+
# https://www.percona.com/doc/percona-toolkit/2.0/pt-online-schema-change.html#dsn-options
|
20
|
+
def to_s
|
21
|
+
"D=#{database},t=#{table_name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :table_name, :database
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generates the equivalent Percona's pt-online-schema-change command to the
|
30
|
+
# given SQL statement
|
31
|
+
class CliGenerator # Command
|
32
|
+
BASE_COMMAND = 'pt-online-schema-change'
|
33
|
+
BASE_OPTIONS = %w(
|
34
|
+
--execute
|
35
|
+
--statistics
|
36
|
+
--recursion-method=none
|
37
|
+
--alter-foreign-keys-method=auto
|
38
|
+
)
|
39
|
+
|
40
|
+
# Constructor
|
41
|
+
#
|
42
|
+
# @param connection_data [Hash]
|
43
|
+
def initialize(connection_data)
|
44
|
+
@connection_data = connection_data
|
45
|
+
init_base_command
|
46
|
+
add_connection_details
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generates the percona command. Fills all the connection credentials from
|
50
|
+
# the current AR connection, but that can amended via ENV-vars:
|
51
|
+
# PERCONA_DB_HOST, PERCONA_DB_USER, PERCONA_DB_PASSWORD, PERCONA_DB_NAME
|
52
|
+
# Table name can't not be amended, it populates automatically from the
|
53
|
+
# migration data
|
54
|
+
#
|
55
|
+
# @param table_name [String]
|
56
|
+
# @param statement [String] MySQL statement
|
57
|
+
# @return [String]
|
58
|
+
def generate(table_name, statement)
|
59
|
+
dsn = DSN.new(database, table_name)
|
60
|
+
alter_argument = AlterArgument.new(statement)
|
61
|
+
|
62
|
+
"#{to_s} #{dsn} #{alter_argument}"
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_reader :connection_data
|
68
|
+
|
69
|
+
# Sets up the command with its options
|
70
|
+
def init_base_command
|
71
|
+
@command = [BASE_COMMAND, BASE_OPTIONS.join(' ')]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Adds the host, user and password, if present, to the command
|
75
|
+
def add_connection_details
|
76
|
+
@command.push("-h #{host}")
|
77
|
+
@command.push("-u #{user}")
|
78
|
+
@command.push("-p #{password}") if password.present?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the command as a string that can be executed in a shell
|
82
|
+
def to_s
|
83
|
+
@command.join(' ')
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns the database host name, defaulting to localhost. If PERCONA_DB_HOST
|
87
|
+
# is passed its value will be used instead
|
88
|
+
#
|
89
|
+
# @return [String]
|
90
|
+
def host
|
91
|
+
ENV['PERCONA_DB_HOST'] || connection_data[:host] || 'localhost'
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the database user. If PERCONA_DB_USER is passed its value will be
|
95
|
+
# used instead
|
96
|
+
#
|
97
|
+
# @return [String]
|
98
|
+
def user
|
99
|
+
ENV['PERCONA_DB_USER'] || connection_data[:username]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the database user's password. If PERCONA_DB_PASSWORD is passed its
|
103
|
+
# value will be used instead
|
104
|
+
#
|
105
|
+
# @return [String]
|
106
|
+
def password
|
107
|
+
ENV['PERCONA_DB_PASSWORD'] || connection_data[:password]
|
108
|
+
end
|
109
|
+
|
110
|
+
# TODO: Doesn't the abstract adapter already handle this somehow?
|
111
|
+
# Returns the database name. If PERCONA_DB_NAME is passed its value will be
|
112
|
+
# used instead
|
113
|
+
def database
|
114
|
+
ENV['PERCONA_DB_NAME'] || connection_data[:database]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'percona_migrator'
|
2
|
+
require 'lhm' # It's our own Lhm adapter, not the gem
|
3
|
+
require 'rails'
|
4
|
+
|
5
|
+
module PerconaMigrator
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
railtie_name :percona_migrator
|
8
|
+
|
9
|
+
# It drops all previous database connections and reconnects using this
|
10
|
+
# PerconaAdapter. By doing this, all later ActiveRecord methods called in
|
11
|
+
# the migration will use this adapter instead of Mysql2Adapter.
|
12
|
+
#
|
13
|
+
# It also patches ActiveRecord's #migrate method so that it patches LHM
|
14
|
+
# first. This will make migrations written with LHM to go through the
|
15
|
+
# regular Rails Migration DSL.
|
16
|
+
initializer 'percona_migrator.configure_rails_initialization' do
|
17
|
+
ActiveSupport.on_load(:active_record) do
|
18
|
+
|
19
|
+
ActiveRecord::Migration.class_eval do
|
20
|
+
alias_method :original_migrate, :migrate
|
21
|
+
|
22
|
+
# Replaces the current connection adapter with the PerconaAdapter and
|
23
|
+
# patches LHM, then it continues with the regular migration process.
|
24
|
+
#
|
25
|
+
# @param direction [Symbol] :up or :down
|
26
|
+
def migrate(direction)
|
27
|
+
reconnect_with_percona
|
28
|
+
::Lhm.migration = self
|
29
|
+
original_migrate(direction)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Make all connections in the connection pool to use PerconaAdapter
|
33
|
+
# instead of the current adapter.
|
34
|
+
def reconnect_with_percona
|
35
|
+
connection_config = ActiveRecord::Base.connection_config
|
36
|
+
ActiveRecord::Base.establish_connection(
|
37
|
+
connection_config.merge(adapter: 'percona')
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module PerconaMigrator
|
4
|
+
|
5
|
+
# It executes pt-online-schema-change commands in a new process and gets its
|
6
|
+
# output and status
|
7
|
+
class Runner
|
8
|
+
|
9
|
+
NONE = "\e[0m"
|
10
|
+
CYAN = "\e[38;5;86m"
|
11
|
+
GREEN = "\e[32m"
|
12
|
+
RED = "\e[31m"
|
13
|
+
|
14
|
+
# Executes the given command printing the output to the logger
|
15
|
+
#
|
16
|
+
# @param command [String]
|
17
|
+
# @param logger [IO]
|
18
|
+
def self.execute(command, logger)
|
19
|
+
new(command, logger).execute
|
20
|
+
end
|
21
|
+
|
22
|
+
# Constructor
|
23
|
+
#
|
24
|
+
# @param logger [IO]
|
25
|
+
def initialize(logger)
|
26
|
+
@logger = logger
|
27
|
+
@status = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Runs and logs the given command
|
31
|
+
#
|
32
|
+
# @param command [String]
|
33
|
+
# @return [Boolean]
|
34
|
+
def execute(command)
|
35
|
+
@command = command
|
36
|
+
|
37
|
+
log_started
|
38
|
+
run_command
|
39
|
+
log_finished
|
40
|
+
|
41
|
+
status
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :command, :logger, :status
|
47
|
+
|
48
|
+
# TODO: log as a migration logger subitem
|
49
|
+
#
|
50
|
+
# Logs when the execution started
|
51
|
+
def log_started
|
52
|
+
logger.puts "\n#{CYAN}-- #{command}#{NONE}\n\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Executes the command outputing any errors
|
56
|
+
#
|
57
|
+
# @raise [Errno::ENOENT] if pt-online-schema-change can't be found
|
58
|
+
def run_command
|
59
|
+
Open3.popen2(command) do |_stdin, stdout, process|
|
60
|
+
@status = process.value
|
61
|
+
logger.puts stdout.read
|
62
|
+
end
|
63
|
+
|
64
|
+
if status.nil?
|
65
|
+
Kernel.warn("Error running '#{command}': status could not be retrieved")
|
66
|
+
end
|
67
|
+
|
68
|
+
if status && status.signaled?
|
69
|
+
Kernel.warn("Error running '#{command}': #{status.to_s}")
|
70
|
+
end
|
71
|
+
|
72
|
+
rescue Errno::ENOENT
|
73
|
+
raise Errno::ENOENT, "Please install pt-online-schema-change. Check: https://www.percona.com/doc/percona-toolkit"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Logs the status of the execution once it's finished
|
77
|
+
def log_finished
|
78
|
+
return unless status
|
79
|
+
|
80
|
+
value = status.exitstatus
|
81
|
+
return unless value
|
82
|
+
|
83
|
+
message = value.zero? ? "#{GREEN}Done!#{NONE}" : "#{RED}Failed!#{NONE}"
|
84
|
+
|
85
|
+
logger.puts("\n#{message}")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
require 'percona_migrator/version'
|
5
|
+
require 'percona_migrator/runner'
|
6
|
+
require 'percona_migrator/cli_generator'
|
7
|
+
|
8
|
+
require 'percona_migrator/railtie' if defined?(Rails)
|
9
|
+
|
10
|
+
module PerconaMigrator
|
11
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'percona_migrator/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'percona_migrator'
|
9
|
+
spec.version = PerconaMigrator::VERSION
|
10
|
+
spec.authors = ['Ilya Zayats', 'Pau Pérez', 'Fran Casas']
|
11
|
+
spec.email = ['ilya.zayats@redbooth.com', 'pau.perez@redbooth.com', 'fran.casas@redbooth.com']
|
12
|
+
|
13
|
+
spec.summary = %q{pt-online-schema-change runner for ActiveRecord migrations}
|
14
|
+
spec.description = %q{Execute your ActiveRecord migrations with Percona's pt-online-schema-change}
|
15
|
+
spec.homepage = 'http://github.com/redbooth/percona_migrator'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
# TODO: Relax me
|
22
|
+
spec.add_runtime_dependency 'rails', '~>3.2.22'
|
23
|
+
spec.add_runtime_dependency 'mysql2', '0.3.20'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.4', '>= 3.4.0'
|
28
|
+
spec.add_development_dependency 'rspec-its', '~> 1.2'
|
29
|
+
spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.1'
|
30
|
+
end
|
data/test_database.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class TestDatabase
|
2
|
+
def initialize(config)
|
3
|
+
@config = config
|
4
|
+
end
|
5
|
+
|
6
|
+
# Creates the percona_migrator_test database and comments table in it
|
7
|
+
def create_test_database
|
8
|
+
%x(#{mysql_command} "DROP DATABASE IF EXISTS percona_migrator_test; CREATE DATABASE percona_migrator_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci")
|
9
|
+
%x(#{mysql_command} "USE percona_migrator_test; DROP TABLE IF EXISTS comments; CREATE TABLE comments (id int(12) NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;")
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates the ActiveRecord's schema_migrations table required for
|
13
|
+
# migrations to work
|
14
|
+
def create_schema_migrations_table
|
15
|
+
%x(#{mysql_command} "USE percona_migrator_test; DROP TABLE IF EXISTS schema_migrations; CREATE TABLE schema_migrations ( version varchar(255) COLLATE utf8_unicode_ci NOT NULL, UNIQUE KEY unique_schema_migrations (version)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci")
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :config
|
21
|
+
|
22
|
+
def mysql_command
|
23
|
+
"mysql --user=#{config['username']} --password=#{config['password']} -e"
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: percona_migrator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.rc.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ilya Zayats
|
8
|
+
- Pau Pérez
|
9
|
+
- Fran Casas
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2016-03-01 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.22
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 3.2.22
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: mysql2
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - '='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.3.20
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - '='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.3.20
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: bundler
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.10'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.10'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rake
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '10.0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '10.0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rspec
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.4'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 3.4.0
|
81
|
+
type: :development
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.4'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 3.4.0
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rspec-its
|
93
|
+
requirement: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '1.2'
|
98
|
+
type: :development
|
99
|
+
prerelease: false
|
100
|
+
version_requirements: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '1.2'
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: byebug
|
107
|
+
requirement: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '8.2'
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 8.2.1
|
115
|
+
type: :development
|
116
|
+
prerelease: false
|
117
|
+
version_requirements: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '8.2'
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 8.2.1
|
125
|
+
description: Execute your ActiveRecord migrations with Percona's pt-online-schema-change
|
126
|
+
email:
|
127
|
+
- ilya.zayats@redbooth.com
|
128
|
+
- pau.perez@redbooth.com
|
129
|
+
- fran.casas@redbooth.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- ".gitignore"
|
135
|
+
- ".rspec"
|
136
|
+
- ".travis.yml"
|
137
|
+
- CHANGELOG.md
|
138
|
+
- CODE_OF_CONDUCT.md
|
139
|
+
- Gemfile
|
140
|
+
- LICENSE.txt
|
141
|
+
- README.md
|
142
|
+
- Rakefile
|
143
|
+
- bin/console
|
144
|
+
- bin/setup
|
145
|
+
- config.yml
|
146
|
+
- configuration.rb
|
147
|
+
- lib/active_record/connection_adapters/percona_adapter.rb
|
148
|
+
- lib/lhm.rb
|
149
|
+
- lib/lhm/adapter.rb
|
150
|
+
- lib/lhm/column_with_sql.rb
|
151
|
+
- lib/lhm/column_with_type.rb
|
152
|
+
- lib/percona_migrator.rb
|
153
|
+
- lib/percona_migrator/alter_argument.rb
|
154
|
+
- lib/percona_migrator/cli_generator.rb
|
155
|
+
- lib/percona_migrator/railtie.rb
|
156
|
+
- lib/percona_migrator/runner.rb
|
157
|
+
- lib/percona_migrator/version.rb
|
158
|
+
- percona_migrator.gemspec
|
159
|
+
- test_database.rb
|
160
|
+
homepage: http://github.com/redbooth/percona_migrator
|
161
|
+
licenses:
|
162
|
+
- MIT
|
163
|
+
metadata: {}
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
|
+
requirements:
|
175
|
+
- - ">"
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
version: 1.3.1
|
178
|
+
requirements: []
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 2.2.2
|
181
|
+
signing_key:
|
182
|
+
specification_version: 4
|
183
|
+
summary: pt-online-schema-change runner for ActiveRecord migrations
|
184
|
+
test_files: []
|
185
|
+
has_rdoc:
|