Migrations
This guide explains how to create and structure database migrations using db-migrate
.
Overview
Migrations in db-migrate
are Ruby blocks that define schema changes. Each migration runs inside a database transaction, ensuring consistency and allowing rollback on errors.
Basic Migration Structure
DB::Migrate.migrate("migration_name", client) do
# Schema operations go here
end
Migration Naming
Use descriptive names that indicate what the migration does:
# Good examples
DB::Migrate.migrate("create_users_table", client) do
# ...
end
DB::Migrate.migrate("add_email_index_to_users", client) do
# ...
end
DB::Migrate.migrate("remove_deprecated_columns", client) do
# ...
end
Transaction Safety
All migration operations run inside a database transaction. If any operation fails, the entire migration is rolled back:
DB::Migrate.migrate("complex_migration", client) do
create_table :users do
primary_key
column :name, "TEXT NOT NULL"
end
# If this fails, the table creation above is rolled back
create_index :users, :name
end
Database Compatibility
db-migrate
automatically detects your database's capabilities and generates appropriate SQL:
PostgreSQL
- Uses
BIGSERIAL
for auto-increment columns - Supports
IF EXISTS
clauses - Uses
ALTER COLUMN ... TYPE ... USING ...
for column type changes
MariaDB/MySQL
- Uses
BIGINT AUTO_INCREMENT
for auto-increment columns - Supports
IF EXISTS
clauses - Uses
MODIFY COLUMN
for column type changes
Available Operations
Table Operations
create_table(name)
- Create a new tablecreate_table?(name)
- Create table only if it doesn't existdrop_table(name, if_exists: true)
- Drop a tablerename_table(old_name, new_name)
- Rename a table
Column Operations
add_column(name, type)
- Add a new columndrop_column(name, if_exists: true)
- Remove a columnrename_column(old_name, new_name)
- Rename a columnchange_column(name, new_type)
- Change column type
Index Operations
create_index(table, columns)
- Create an indexdrop_index(name, if_exists: true)
- Drop an index
Information Schema Access
Query database metadata within migrations:
DB::Migrate.migrate("conditional_migration", client) do
# Check if table exists before creating
unless information_schema.table_exists?(:users)
create_table :users do
primary_key
column :name, "TEXT NOT NULL"
end
end
end