mysql_online_migrations 0.1.3

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 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