mysql_online_migrations 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8b8c18f26ec8b8f57c2d469d2e11e3837de24130
4
+ data.tar.gz: 3a65090310d0a049c85ad22da8099ea46939a913
5
+ SHA512:
6
+ metadata.gz: 3bb7753d1b88a3a77cddf0f23a2a2b6cfe3717821dc625b0fb5024bd4cce25fc45aecd66a75cbb1e8b16464fb8cdd11da1e6b548e57a2c38adc7ec0dd2e77c6a
7
+ data.tar.gz: a631129c1c90cddd1528658c37a815749a0954882fdf8ddcf387bdf1f7c555fd63dd5ecf18ca2b60443833a9df33fdd96f98a2bfe6a08f7c3e8691b09d544f76
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+
20
+ .DS_Store
21
+ *.swp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mysql_online_migrations (0.1.2)
5
+ activerecord (~> 3.2.15)
6
+ activesupport (~> 3.2.15)
7
+ mysql2
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activemodel (3.2.15)
13
+ activesupport (= 3.2.15)
14
+ builder (~> 3.0.0)
15
+ activerecord (3.2.15)
16
+ activemodel (= 3.2.15)
17
+ activesupport (= 3.2.15)
18
+ arel (~> 3.0.2)
19
+ tzinfo (~> 0.3.29)
20
+ activesupport (3.2.15)
21
+ i18n (~> 0.6, >= 0.6.4)
22
+ multi_json (~> 1.0)
23
+ arel (3.0.3)
24
+ builder (3.0.4)
25
+ coderay (1.0.9)
26
+ diff-lcs (1.2.5)
27
+ i18n (0.6.5)
28
+ logger (1.2.8)
29
+ method_source (0.8.2)
30
+ multi_json (1.8.2)
31
+ mysql2 (0.3.14)
32
+ pry (0.9.12.2)
33
+ coderay (~> 1.0.5)
34
+ method_source (~> 0.8)
35
+ slop (~> 3.4)
36
+ rspec (2.14.1)
37
+ rspec-core (~> 2.14.0)
38
+ rspec-expectations (~> 2.14.0)
39
+ rspec-mocks (~> 2.14.0)
40
+ rspec-core (2.14.7)
41
+ rspec-expectations (2.14.4)
42
+ diff-lcs (>= 1.1.3, < 2.0)
43
+ rspec-mocks (2.14.4)
44
+ slop (3.4.6)
45
+ tzinfo (0.3.38)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ logger
52
+ mysql_online_migrations!
53
+ pry
54
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Anthony Alberto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ mysql_online_migrations
2
+ =======================
3
+
4
+ Patch Rails migrations to support MySQL 5.6 online migrations capabilities.
5
+ Prior to MySQL 5.6, when adding / removing / renaming indexes and columns, MySQL would lock the writes of the whole table.
6
+ MySQL 5.6 adds a way to append `LOCK=NONE` to those alter table statements to allow online migrations.
7
+
8
+
9
+ Requirements
10
+ =======================
11
+ Built for Rails 3.2.15, may be compatible with Rails 4.0 but you'd lose the new features introduced.
12
+ This gem actually just requires ActiveRecord and ActiveSupport, the full Rails gem should not be required.
13
+
14
+ List of requirements :
15
+
16
+ - Use mysql2 adapter
17
+ - Use ActiveRecord "~> 3.2.15"
18
+ - Use MySQL or Percona Server 5.6.X
19
+
20
+ Scope of this gem
21
+ =======================
22
+
23
+ Patch Rails migrations to automatically add `LOCK=NONE` in the following context :
24
+
25
+ - Index management : `add_index`, `remove_index`, `rename_index`
26
+ - Add column : `add_column`, `add_timestamps`
27
+ - Remove column : `remove_column`, `remove_timestamps`
28
+
29
+ Usage
30
+ =======================
31
+ In a typical Rails app, just add it to your gem file :
32
+ `gem 'mysql_online_migrations'`
33
+
34
+ And you're ready for online migrations!
35
+
36
+ ### Turn it off for a whole environment
37
+ Example for environment test (your CI might not use MySQL 5.6 yet), add the following to `config/environments/test.rb`:
38
+ `config.active_record.mysql_online_migrations = false`
39
+
40
+ ### Turn it off for a specific statement
41
+ Add `lock: true` to any of the method calls mentioned above. Example :
42
+ `add_index :users, :name, lock: true`
43
+
44
+ The `lock: none` will be useful when hitting the caveats of `LOCK=NONE`. Please read the following section.
45
+
46
+ Caveats
47
+ =======================
48
+
49
+ Here's a list of things you can't do with LOCK=NONE and therefore you should provide `lock: true`:
50
+
51
+ - Index a column of type text
52
+ - Change the type of a column
53
+ - Change the length of a column
54
+ - Change the nullable value of a column
55
+ - When adding an AUTO_INCREMENT column,
56
+ - Other stuff found on https://blogs.oracle.com/mysqlinnodb/entry/online_alter_table_in_mysql :
57
+ - when the table contains FULLTEXT indexes or a hidden FTS_DOC_ID column, or
58
+ - when there are FOREIGN KEY constraints referring to the table, with ON…CASCADE or ON…SET NULL option.
59
+
60
+ If you don't use `lock: true` when it's not supported, you'll get a MySQL exception. No risk to lock the table by accident.
@@ -0,0 +1,33 @@
1
+ require 'active_record'
2
+ require "active_record/connection_adapters/mysql2_adapter"
3
+
4
+ %w(*.rb).each do |path|
5
+ Dir["#{File.dirname(__FILE__)}/mysql_online_migrations/#{path}"].each { |f| require(f) }
6
+ end
7
+
8
+ module MysqlOnlineMigrations
9
+ include Indexes
10
+ include Columns
11
+
12
+ def self.included(base)
13
+ ActiveRecord::Base.send(:class_attribute, :mysql_online_migrations, :instance_writer => false)
14
+ ActiveRecord::Base.send("mysql_online_migrations=", true)
15
+ end
16
+
17
+ def lock_statement(lock, with_comma = false)
18
+ return "" if lock == true
19
+ return "" unless perform_migrations_online?
20
+ puts "ONLINE MIGRATION"
21
+ "#{with_comma ? ', ' : ''} LOCK=NONE"
22
+ end
23
+
24
+ def extract_lock_from_options(options)
25
+ [options[:lock], options.except(:lock)]
26
+ end
27
+
28
+ def perform_migrations_online?
29
+ ActiveRecord::Base.mysql_online_migrations == true
30
+ end
31
+ end
32
+
33
+ ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(:include, MysqlOnlineMigrations)
@@ -0,0 +1,48 @@
1
+ module MysqlOnlineMigrations
2
+ module Columns
3
+ def add_column(table_name, column_name, type, options = {})
4
+ lock, options = extract_lock_from_options(options)
5
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)} #{lock_statement(lock, true)}")
6
+ end
7
+
8
+ def add_timestamps(table_name, options = {})
9
+ add_column table_name, :created_at, :datetime, options
10
+ add_column table_name, :updated_at, :datetime, options
11
+ end
12
+
13
+ def change_column(table_name, column_name, type, options = {})
14
+ lock, options = extract_lock_from_options(options)
15
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)} #{lock_statement(lock, true)}")
16
+ end
17
+
18
+ def rename_column(table_name, column_name, new_column_name, options = {})
19
+ lock, options = extract_lock_from_options(options)
20
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)} #{lock_statement(lock, true)}")
21
+ end
22
+
23
+ def remove_column(table_name, *column_names)
24
+ if column_names.flatten!
25
+ message = 'Passing array to remove_columns is deprecated, please use ' +
26
+ 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
27
+ ActiveSupport::Deprecation.warn message, caller
28
+ end
29
+
30
+ lock, options = if column_names.last.is_a? Hash
31
+ options = column_names.last
32
+ column_names = column_names[0..-2]
33
+ extract_lock_from_options(options)
34
+ else
35
+ [false, {}]
36
+ end
37
+
38
+ columns_for_remove(table_name, *column_names).each do |column_name|
39
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name} #{lock_statement(lock, true)}"
40
+ end
41
+ end
42
+
43
+ def remove_timestamps(table_name, options = {})
44
+ remove_column table_name, :updated_at, options
45
+ remove_column table_name, :created_at, options
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module MysqlOnlineMigrations
2
+ module Indexes
3
+ def add_index(table_name, column_name, options = {})
4
+ lock, options = extract_lock_from_options(options)
5
+ index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
6
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns}) #{lock_statement(lock)}"
7
+ end
8
+
9
+ def remove_index(table_name, options_index_name, options = {})
10
+ lock, options = extract_lock_from_options(options)
11
+ execute "DROP INDEX #{quote_column_name(index_name_for_remove(table_name, options_index_name))} ON #{quote_table_name(table_name)} #{lock_statement(lock)}"
12
+ end
13
+
14
+ def rename_index(table_name, old_name, new_name, options = {})
15
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
16
+ return unless old_index_def
17
+ remove_index(table_name, { :name => old_name }, options)
18
+ add_index(table_name, old_index_def.columns, options.merge(name: new_name, unique: old_index_def.unique))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mysql_online_migrations'
3
+ s.version = '0.1.3'
4
+ s.summary = "Use MySQL 5.6+ capacities to perform online migrations"
5
+ s.description = "MySQL 5.6 adds a way to append `LOCK=NONE` to alter table statements to allow online migrations. Let's use it."
6
+ s.authors = ["Anthony Alberto"]
7
+ s.email = 'alberto.anthony@gmail.com'
8
+ s.homepage = 'https://github.com/anthonyalberto/mysql_online_migrations'
9
+
10
+ s.add_runtime_dependency "activerecord", "~> 3.2.15"
11
+ s.add_runtime_dependency "activesupport", "~> 3.2.15"
12
+ s.add_runtime_dependency "mysql2"
13
+ s.add_development_dependency "logger"
14
+ s.add_development_dependency "rspec"
15
+ s.add_development_dependency "pry"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+
3
+ describe MysqlOnlineMigrations::Columns do
4
+ let(:comma_before_lock_none) { true }
5
+
6
+ context "#add_column" do
7
+ let(:queries) do
8
+ {
9
+ [:testing, :foo2, :string, {}] =>
10
+ "ALTER TABLE `testing` ADD `foo2` varchar(255)",
11
+ [:testing, :foo2, :string, { limit: 20, null: false, default: 'def' }] =>
12
+ "ALTER TABLE `testing` ADD `foo2` varchar(20) DEFAULT 'def' NOT NULL",
13
+ [:testing, :foo2, :decimal, { precision:3, scale: 2 }] =>
14
+ "ALTER TABLE `testing` ADD `foo2` decimal(3,2)",
15
+ }
16
+ end
17
+
18
+ let(:method_name) { :add_column }
19
+
20
+ it_behaves_like "a method that adds LOCK=NONE when needed"
21
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
22
+ end
23
+
24
+ context "#add_timestamps" do
25
+ let(:queries) do
26
+ {
27
+ [:testing2, {}] =>
28
+ [
29
+ "ALTER TABLE `testing2` ADD `created_at` datetime",
30
+ "ALTER TABLE `testing2` ADD `updated_at` datetime",
31
+ ]
32
+ }
33
+ end
34
+
35
+ let(:method_name) { :add_timestamps }
36
+
37
+ it_behaves_like "a method that adds LOCK=NONE when needed"
38
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
39
+ end
40
+
41
+ context "#remove_column" do
42
+ let(:queries) do
43
+ {
44
+ [:testing, :foo, {}] =>
45
+ "ALTER TABLE `testing` DROP `foo`",
46
+ [:testing, [:foo, :bar], {}] =>
47
+ [
48
+ "ALTER TABLE `testing` DROP `foo`",
49
+ "ALTER TABLE `testing` DROP `bar`"
50
+ ],
51
+ [:testing, :foo, :bar, {}] =>
52
+ [
53
+ "ALTER TABLE `testing` DROP `foo`",
54
+ "ALTER TABLE `testing` DROP `bar`"
55
+ ]
56
+ }
57
+ end
58
+
59
+ let(:method_name) { :remove_column }
60
+
61
+ it_behaves_like "a method that adds LOCK=NONE when needed"
62
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
63
+ end
64
+
65
+ context "#remove_timestamps" do
66
+ let(:queries) do
67
+ {
68
+ [:testing, {}] =>
69
+ [
70
+ "ALTER TABLE `testing` DROP `created_at`",
71
+ "ALTER TABLE `testing` DROP `updated_at`",
72
+ ]
73
+ }
74
+ end
75
+
76
+ let(:method_name) { :remove_timestamps }
77
+
78
+ it_behaves_like "a method that adds LOCK=NONE when needed"
79
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
80
+ end
81
+
82
+ context "#change_column" do
83
+ let(:queries) do
84
+ # Unsupported with lock=none : change column type, change limit, change null
85
+ {
86
+ [:testing, :foo, :string, { default: 'def', limit: 100 }] =>
87
+ "ALTER TABLE `testing` CHANGE `foo` `foo` varchar(100) DEFAULT 'def'",
88
+ }
89
+ end
90
+
91
+ let(:method_name) { :change_column }
92
+
93
+ it_behaves_like "a method that adds LOCK=NONE when needed"
94
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
95
+ end
96
+
97
+ context "#rename_column" do
98
+ let(:queries) do
99
+ {
100
+ [:testing, :foo, :foo2, {}] =>
101
+ "ALTER TABLE `testing` CHANGE `foo` `foo2` varchar(100) DEFAULT NULL"
102
+ }
103
+ end
104
+
105
+ let(:method_name) { :rename_column }
106
+
107
+ it_behaves_like "a method that adds LOCK=NONE when needed"
108
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
109
+ end
110
+ end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+
3
+ describe MysqlOnlineMigrations::Indexes do
4
+ let(:comma_before_lock_none) { false }
5
+
6
+ context "#add_index" do
7
+ let(:queries) do
8
+ {
9
+ [:testing, :foo, {}] =>
10
+ "CREATE INDEX `index_testing_on_foo` ON `testing` (`foo`)",
11
+ [:testing, :foo, { length: 10 }] =>
12
+ "CREATE INDEX `index_testing_on_foo` ON `testing` (`foo`(10))",
13
+ [:testing, [:foo, :bar, :baz], {}] =>
14
+ "CREATE INDEX `index_testing_on_foo_and_bar_and_baz` ON `testing` (`foo`, `bar`, `baz`)",
15
+ [:testing, [:foo, :bar, :baz], { unique: true }] =>
16
+ "CREATE UNIQUE INDEX `index_testing_on_foo_and_bar_and_baz` ON `testing` (`foo`, `bar`, `baz`)",
17
+ [:testing, [:foo, :bar, :baz], { unique: true, name: "best_index_of_the_world" }] =>
18
+ "CREATE UNIQUE INDEX `best_index_of_the_world` ON `testing` (`foo`, `bar`, `baz`)",
19
+ }
20
+ end
21
+
22
+ let(:method_name) { :add_index }
23
+
24
+ it_behaves_like "a method that adds LOCK=NONE when needed"
25
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
26
+ end
27
+
28
+ context "#remove_index" do
29
+ let(:queries) do
30
+ {
31
+ [:testing, :baz, {}] =>
32
+ "DROP INDEX `index_testing_on_baz` ON `testing`",
33
+ [:testing, [:bar, :baz], {}] =>
34
+ "DROP INDEX `index_testing_on_bar_and_baz` ON `testing`",
35
+ [:testing, { column: [:bar, :baz] }, {}] =>
36
+ "DROP INDEX `index_testing_on_bar_and_baz` ON `testing`",
37
+ [:testing, { name: "best_index_of_the_world2" }, {}] =>
38
+ "DROP INDEX `best_index_of_the_world2` ON `testing`"
39
+ }
40
+ end
41
+ let(:method_name) { :remove_index }
42
+
43
+ it_behaves_like "a method that adds LOCK=NONE when needed"
44
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
45
+ end
46
+
47
+ context "#rename_index" do
48
+ let(:queries) do
49
+ {
50
+ [:testing, "best_index_of_the_world2", "renamed_best_index_of_the_world2", {}] =>
51
+ [
52
+ "DROP INDEX `best_index_of_the_world2` ON `testing`",
53
+ "CREATE INDEX `renamed_best_index_of_the_world2` ON `testing` (`extra`)",
54
+ ],
55
+ [:testing, "best_index_of_the_world3", "renamed_best_index_of_the_world3", {}] =>
56
+ [
57
+ "DROP INDEX `best_index_of_the_world3` ON `testing`",
58
+ "CREATE UNIQUE INDEX `renamed_best_index_of_the_world3` ON `testing` (`baz`, `extra`)",
59
+ ]
60
+ }
61
+ end
62
+ let(:method_name) { :rename_index }
63
+
64
+ it_behaves_like "a method that adds LOCK=NONE when needed"
65
+ it_behaves_like "a request with LOCK=NONE that doesn't crash in MySQL"
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ # require 'active_record'
8
+ require 'rubygems'
9
+ require 'bundler/setup'
10
+ require 'mysql_online_migrations'
11
+ require 'logger'
12
+ require 'pry'
13
+ require 'support/helpers'
14
+ require 'support/shared_examples/mysql_online_migrations'
15
+
16
+ RSpec.configure do |config|
17
+ config.treat_symbols_as_metadata_keys_with_true_values = true
18
+ config.run_all_when_everything_filtered = true
19
+ config.filter_run :focus
20
+
21
+ config.include Helpers
22
+
23
+ # Run specs in random order to surface order dependencies. If you find an
24
+ # order dependency and want to debug it, you can fix the order by providing
25
+ # the seed, which is printed after each run.
26
+ # --seed 1234
27
+ #config.order = 'random'
28
+
29
+ config.before(:all) do
30
+ setup
31
+ end
32
+
33
+ config.after(:all) do
34
+ teardown
35
+ end
36
+
37
+ end
@@ -0,0 +1,73 @@
1
+ module Helpers
2
+ def execute(statement)
3
+ end
4
+
5
+ def unstub_execute
6
+ @adapter.unstub(:execute)
7
+ end
8
+
9
+ def stub_execute
10
+ original_execute = @adapter.method(:execute)
11
+ @adapter.stub(:execute) do |statement|
12
+ if statement =~ /^(alter|create|drop) /i
13
+ execute(statement.squeeze(' ').strip)
14
+ else
15
+ original_execute.call(statement)
16
+ end
17
+ end
18
+ end
19
+
20
+ def add_lock_none(str, with_comma)
21
+ "#{str}#{with_comma ? ' ,' : ''} LOCK=NONE"
22
+ end
23
+
24
+ def rebuild_table
25
+ @table_name = :testing
26
+ @adapter.drop_table @table_name rescue nil
27
+ @adapter.create_table @table_name do |t|
28
+ t.column :foo, :string, :limit => 100
29
+ t.column :bar, :string, :limit => 100
30
+ t.column :baz, :string, :limit => 100
31
+ t.column :extra, :string, :limit => 100
32
+ t.timestamps
33
+ end
34
+
35
+ @table_name = :testing2
36
+ @adapter.drop_table @table_name rescue nil
37
+ @adapter.create_table @table_name do |t|
38
+ end
39
+
40
+ @adapter.add_index :testing, :baz
41
+ @adapter.add_index :testing, [:bar, :baz]
42
+ @adapter.add_index :testing, :extra, name: "best_index_of_the_world2"
43
+ @adapter.add_index :testing, [:baz, :extra], name: "best_index_of_the_world3", unique: true
44
+ end
45
+
46
+ def setup
47
+ ActiveRecord::Base.establish_connection(
48
+ adapter: :mysql2,
49
+ reconnect: false,
50
+ database: "mysql_online_migrations",
51
+ username: "root",
52
+ host: "localhost",
53
+ encoding: "utf8",
54
+ socket: "/tmp/mysql.sock"
55
+ )
56
+
57
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
58
+ ActiveRecord::Base.logger.level = Logger::INFO
59
+
60
+ @adapter = ActiveRecord::Base.connection
61
+
62
+ rebuild_table
63
+ end
64
+
65
+ def set_ar_setting(value)
66
+ ActiveRecord::Base.stub(:mysql_online_migrations).and_return(value)
67
+ end
68
+
69
+ def teardown
70
+ @adapter.drop_table :testing rescue nil
71
+ ActiveRecord::Base.primary_key_prefix_type = nil
72
+ end
73
+ end
@@ -0,0 +1,44 @@
1
+ shared_examples_for "a method that adds LOCK=NONE when needed" do
2
+ before(:each) do
3
+ stub_execute
4
+ end
5
+
6
+ it "adds LOCK=NONE at the end of the query" do
7
+ queries.each do |arguments, output|
8
+ Array.wrap(output).each { |out| should_receive(:execute).with(add_lock_none(out, comma_before_lock_none)) }
9
+ @adapter.public_send(method_name, *arguments)
10
+ end
11
+ end
12
+
13
+ context "with AR global setting set to false" do
14
+ before(:each) { set_ar_setting(false) }
15
+
16
+ it "doesn't add lock none" do
17
+ queries.each do |arguments, output|
18
+ Array.wrap(output).each { |out| should_receive(:execute).with(out) }
19
+ @adapter.public_send(method_name, *arguments)
20
+ end
21
+ end
22
+ end
23
+
24
+ context "with lock: true option" do
25
+ it "doesn't add lock none" do
26
+ queries.each do |arguments, output|
27
+ Array.wrap(output).each { |out| should_receive(:execute).with(out) }
28
+ arguments[-1] = arguments.last.merge(lock: true)
29
+ @adapter.public_send(method_name, *arguments)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ shared_examples_for "a request with LOCK=NONE that doesn't crash in MySQL" do
36
+ it "succeeds without exception" do
37
+ queries.each do |_, output|
38
+ Array.wrap(output).each do |out|
39
+ @adapter.execute(add_lock_none(out, comma_before_lock_none))
40
+ rebuild_table
41
+ end
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysql_online_migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Anthony Alberto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.15
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.15
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.15
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.15
41
+ - !ruby/object:Gem::Dependency
42
+ name: mysql2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: logger
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: MySQL 5.6 adds a way to append `LOCK=NONE` to alter table statements
98
+ to allow online migrations. Let's use it.
99
+ email: alberto.anthony@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE
109
+ - README.md
110
+ - lib/mysql_online_migrations.rb
111
+ - lib/mysql_online_migrations/columns.rb
112
+ - lib/mysql_online_migrations/indexes.rb
113
+ - mysql_online_migrations.gemspec
114
+ - spec/lib/mysql_online_migrations/columns_spec.rb
115
+ - spec/lib/mysql_online_migrations/indexes_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/helpers.rb
118
+ - spec/support/shared_examples/mysql_online_migrations.rb
119
+ homepage: https://github.com/anthonyalberto/mysql_online_migrations
120
+ licenses: []
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.0.2
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Use MySQL 5.6+ capacities to perform online migrations
142
+ test_files:
143
+ - spec/lib/mysql_online_migrations/columns_spec.rb
144
+ - spec/lib/mysql_online_migrations/indexes_spec.rb
145
+ - spec/spec_helper.rb
146
+ - spec/support/helpers.rb
147
+ - spec/support/shared_examples/mysql_online_migrations.rb