class Lhm::Migrator
Copies existing schema and applies changes using alter on the empty table. `run` returns a Migration
which can be used for the remaining process.
Attributes
Public Class Methods
# File lib/lhm/migrator.rb, line 18 def initialize(table, connection = nil) @connection = connection @origin = table @name = table.destination_name @statements = [] @renames = {} end
Public Instance Methods
Add a column to a table
@example
Lhm.change_table(:users) do |m| m.add_column(:comment, "VARCHAR(12) DEFAULT '0'") end
@param [String] name Name of the column to add @param [String] definition Valid SQL column definition
# File lib/lhm/migrator.rb, line 55 def add_column(name, definition) ddl('alter table `%s` add column `%s` %s' % [@name, name, definition]) end
Add an index to a table
@example
Lhm.change_table(:users) do |m| m.add_index(:comment) m.add_index([:username, :created_at]) m.add_index("comment(10)") end
@param [String, Symbol, Array<String, Symbol>] columns
A column name given as String or Symbol. An Array of Strings or Symbols for compound indexes. It's possible to pass a length limit.
@param [String, Symbol] index_name
Optional name of the index to be created
# File lib/lhm/migrator.rb, line 125 def add_index(columns, index_name = nil) ddl(index_ddl(columns, false, index_name)) end
Add a unique index to a table
@example
Lhm.change_table(:users) do |m| m.add_unique_index(:comment) m.add_unique_index([:username, :created_at]) m.add_unique_index("comment(10)") end
@param [String, Symbol, Array<String, Symbol>] columns
A column name given as String or Symbol. An Array of Strings or Symbols for compound indexes. It's possible to pass a length limit.
@param [String, Symbol] index_name
Optional name of the index to be created
# File lib/lhm/migrator.rb, line 144 def add_unique_index(columns, index_name = nil) ddl(index_ddl(columns, true, index_name)) end
Change an existing column to a new definition
@example
Lhm.change_table(:users) do |m| m.change_column(:comment, "VARCHAR(12) DEFAULT '0' NOT NULL") end
@param [String] name Name of the column to change @param [String] definition Valid SQL column definition
# File lib/lhm/migrator.rb, line 69 def change_column(name, definition) ddl('alter table `%s` modify column `%s` %s' % [@name, name, definition]) end
Alter a table with a custom statement
@example
Lhm.change_table(:users) do |m| m.ddl("ALTER TABLE #{m.name} ADD COLUMN age INT(11)") end
@param [String] statement SQL alter statement @note
Don't write the table name directly into the statement. Use the #name getter instead, because the alter statement will be executed against a temporary table.
# File lib/lhm/migrator.rb, line 41 def ddl(statement) statements << statement end
Filter the data that is copied into the new table by the provided SQL. This SQL will be inserted into the copy directly after the “from” statement - so be sure to use inner/outer join syntax and not cross joins.
@example Add a conditions filter to the migration.
Lhm.change_table(:sounds) do |m| m.filter("inner join users on users.`id` = sounds.`user_id` and sounds.`public` = 1") end
@param [ String ] sql The sql filter.
@return [ String ] The sql filter.
# File lib/lhm/migrator.rb, line 182 def filter(sql) @conditions = sql end
Remove a column from a table
@example
Lhm.change_table(:users) do |m| m.remove_column(:comment) end
@param [String] name Name of the column to delete
# File lib/lhm/migrator.rb, line 106 def remove_column(name) ddl('alter table `%s` drop `%s`' % [@name, name]) end
Remove an index from a table
@example
Lhm.change_table(:users) do |m| m.remove_index(:comment) m.remove_index([:username, :created_at]) end
@param [String, Symbol, Array<String, Symbol>] columns
A column name given as String or Symbol. An Array of Strings or Symbols for compound indexes.
@param [String, Symbol] index_name
Optional name of the index to be removed
# File lib/lhm/migrator.rb, line 162 def remove_index(columns, index_name = nil) columns = [columns].flatten.map(&:to_sym) from_origin = @origin.indices.find { |_, cols| cols.map(&:to_sym) == columns } index_name ||= from_origin[0] unless from_origin.nil? index_name ||= idx_name(@origin.name, columns) ddl('drop index `%s` on `%s`' % [index_name, @name]) end
Rename an existing column.
@example
Lhm.change_table(:users) do |m| m.rename_column(:login, :username) end
@param [String] old Name of the column to change @param [String] nu New name to use for the column
# File lib/lhm/migrator.rb, line 83 def rename_column(old, nu) col = @origin.columns[old.to_s] definition = col[:type] definition += ' NOT NULL' unless col[:is_nullable] == "YES" definition += " DEFAULT #{@connection.quote(col[:column_default])}" if col[:column_default] definition += " COMMENT #{@connection.quote(col[:comment])}" if col[:comment] definition += " COLLATE #{@connection.quote(col[:collate])}" if col[:collate] ddl('alter table `%s` change column `%s` `%s` %s' % [@name, old, nu, definition]) @renames[old.to_s] = nu.to_s end
Private Instance Methods
# File lib/lhm/migrator.rb, line 231 def assert_valid_idx_name(index_name) if index_name && !(index_name.is_a?(String) || index_name.is_a?(Symbol)) raise ArgumentError, 'index_name must be a string or symbol' end end
# File lib/lhm/migrator.rb, line 212 def destination_create original = %{CREATE TABLE `#{ @origin.name }`} replacement = %{CREATE TABLE `#{ @origin.destination_name }`} stmt = @origin.ddl.gsub(original, replacement) @connection.execute(tagged(stmt)) end
# File lib/lhm/migrator.rb, line 219 def destination_read Table.parse(@origin.destination_name, connection) end
# File lib/lhm/migrator.rb, line 204 def execute destination_create @statements.each do |stmt| @connection.execute(tagged(stmt)) end Migration.new(@origin, destination_read, conditions, renames) end
# File lib/lhm/migrator.rb, line 223 def index_ddl(cols, unique = nil, index_name = nil) assert_valid_idx_name(index_name) type = unique ? 'unique index' : 'index' index_name ||= idx_name(@origin.name, cols) parts = [type, index_name, @name, idx_spec(cols)] 'create %s `%s` on `%s` (%s)' % parts end
# File lib/lhm/migrator.rb, line 188 def validate unless @connection.data_source_exists?(@origin.name) error("could not find origin table #{ @origin.name }") end unless @origin.satisfies_id_column_requirement? error('origin does not satisfy `id` key requirements') end dest = @origin.destination_name if @connection.data_source_exists?(dest) error("#{ dest } should not exist; not cleaned up from previous run?") end end