Make your database migrations as easy as Laravel.
$ go install github.com/panda843/go-migrate@latest
Let us quickly start trying to use go-migrate to migrate your database, which uses mysql as an example.
Assume you already installed the package.
- Create an empty directory for the go project and enter it.
$ mkdir go-app && cd go-app
- Init go module.
$ go mod init <module-path>
- Init go-migrate
$ go-migrate init mysql
- Open
cmd/migrate/migrate.go
to set your database config or immediately execute migrate operation.
$ go-migrate migrate
- Finalize, you can see the users table in your mysql database.
The available commands are as follows:
$ go-migrate init <db>
<db>
currently support the following:
- mysql
The init
command will init go-migrate context in your project and create a sample migration file like below.
// omit...
type UsersTable struct{}
func CreateUsersTable() interfaces.Migration {
return &UsersTable{}
}
func (t *UsersTable) Up() error {
return mysql.Schema.Create("users", func(table interfaces.Blueprint) {
table.Id("id", 10)
table.String("username", 100)
table.String("password", 100)
table.Timestamps()
})
}
func (t *UsersTable) Down() error {
return mysql.Schema.DropIfExists("users")
}
Also, you can modify DatabaseConfig in cmd/migrate/migrate.go
.
The default like follows:
config.DatabaseConfig{
Host: "127.0.0.1",
Port: 3306,
Username: "root",
Password: "",
Dbname: "test",
}
$ go-migrate new <filename>
The new
command will create a migration file, filename has the following rules:
- create_
<table>
_table: Generate a migration file, Up() includesSchema.Create
and Down() includesSchema.DropIfExists
. - xxx_to_
<table>
_table: Generate a migration file, Up() and Down() includesSchema.Table
both. - otherwise: Generate a bare migration file which only Up() and Down().
$ go-migrate migrate
The migrate
command will execute all migrate operations, that except already executed migrate and record the state to the database.
$ go-migrate rollback
The rollback
command will rollback migrate of the one batch.
$ go-migrate reset
The reset
command will rollback all migrate operations.
$ go-migrate fresh
The fresh
command will drop all tables and re-run all migrations.
$ go-migrate refresh
The refresh
command will rollback all migrate operations and re-run all migrations.
type Migration struct {
Id int
Migration string
Batch int
}
The model represents a row of migration records.
package model
type Seeder struct {
Err error
}
func (s *Seeder) Error() string {
if s.Err == nil {
return ""
}
return s.Err.Error()
}
func NewSeeder(err error) *Seeder {
return &Seeder{
Err: err,
}
}
This is just a model it implements Error interface, actually it has nothing to do Seeder interface, just convenient customize Seeder, for example below:
Example:
type seeder struct {
*model.Seeder
table string
}
func NewSeeder(table string, err error) interfaces.Seeder {
return &seeder{
Seeder: model.NewSeeder(err),
table: table,
}
}
More information can see interface: Seeder
type Migration interface {
Up() error
Down() error
}
The interface defines that every migration file should implement the function.
- Up: When executing the
migrate
command. - Down: When executing the
rollback
command.
type Migrator interface {
CheckTable() (bool, error)
CreateTable() error
DropTableIfExists() error
DropAllTable() error
GetMigrations() ([]model.Migration, error)
WriteRecord(migration string, batch int) error
DeleteRecord(id int) error
}
The interface defines follows:
Assume use
migrations
as the table for storing migration records.
- CheckTable: Check if the
migrations
table exists. - CreateTable: Create the
migrations
table. - DropTableIfExists: Drop the
migrations
table - DropAllTable: Drop all table.
- GetMigrations: Get all migrations.
- WriteRecord: Write a record to
migrations
table. - DeleteRecord: Delete a record of
migration
table.
type Schema interface {
Create(table string, schemaFunc func(Blueprint)) error
Table(table string, schemaFunc func(Blueprint)) error
DropIfExists(table string) error
}
The interface defines follows:
- Create: Define how to create the table and execute it.
- Table: Define how to alter the table and execute it.
- DropIfExists: Drop table if exists.
package interfaces
type SchemaWithSeeder interface {
Create(table string, schemaFunc func(Blueprint)) Seeder
Table(table string, schemaFunc func(Blueprint)) error
DropIfExists(table string) error
}
This interface basically the same as Schema, but its Create function will return the Seeder interface.
package interfaces
type Seeder interface {
error
Seed(data ...map[string]interface{}) error
}
The interface defines follows:
- error: Because user probably don't use seeder, so it will return this error.
- Seed: Define how to seed.
type Blueprint interface {
Id(name string, length int)
String(name string, length int) Blueprint
Text(name string) Blueprint
Integer(name string, length int) Blueprint
Date(name string) Blueprint
Boolean(name string) Blueprint
DateTime(name string) Blueprint
Nullable() Blueprint
Unique(column ...string) Blueprint
Index(column ...string) Blueprint
Default(value interface{}) Blueprint
Foreign(name string) ForeignBlueprint
Primary(name ...string) Blueprint
DropColumn(column string)
DropUnique(name string)
DropIndex(name string)
DropForeign(name string)
DropPrimary()
Timestamps()
}
The interface defines follows:
- Id: Create an
int
equivalent column and auto increment. - String: Create a
string
equivalent column. - Text: Create a
text
equivalent column. - Integer: Create an
int
equivalent column. - Date: Create a
date
equivalent column. - Boolean: Create a
boolean
equivalent column. - DateTime: Create a
datetime
equivalent column. - Nullable: The columns that are created will be
nullable
- Unique: Create an index, it is unique.
- Index: Create an index.
- Default: Set a default value to the column.
- Foreign: Create a foreign key, more information is below: ForeignBlueprint
- Primary: Create primary key, support composite keys.
- DropColumn: Drop a column.
- DropUnique: Same as DropIndex.
- DropIndex: Drop a index.
- DropForeign: Drop a foreign key and index.
- DropPrimary: Drop primary key.
- Timestamps: Create created_at field with the current time as default and updated_at TIMESTAMP equivalent columns.
type ForeignBlueprint interface {
Reference(name string) ForeignBlueprint
On(table string) ForeignBlueprint
OnUpdate(action string) ForeignBlueprint
OnDelete(action string) ForeignBlueprint
}
The interface defines follows:
- Reference: Set the foreign key reference column.
- On: Set the reference table.
- OnUpdate: Set onUpdate action.
- OnDelete: Set onDelete action.
Assume use mysql as example.
mysql.Schema.Create("users", func(table interfaces.Blueprint) {
table.Id("id", 10)
table.String("username", 100)
table.String("password", 100)
table.Timestamps()
})
The above will generate the following sql:
CREATE TABLE `users`(
`id` INT(10) NOT NULL AUTO_INCREMENT, PRIMARY KEY(`id`),
`username` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT NULL
);
schema.Table("users", func(table interfaces.Blueprint) {
table.Boolean("is_admin")
table.String("email", 100).Unique()
})
The above will generate the following sql:
ALTER TABLE `users`
ADD `is_admin` TINYINT NOT NULL,
ADD `email` VARCHAR(100) NOT NULL,
ADD UNIQUE(`email`);
schema.Table("projects", func(table interfaces.Blueprint) {
table.Integer("user_id", 10)
table.Foreign("user_id").Reference("id").On("users").OnUpdate("cascade")
})
The above will generate the following sql:
ALTER TABLE `projects`
ADD `user_id` INT(10) NOT NULL,
ADD CONSTRAINT `fk_projects_user_id` FOREIGN KEY(`user_id`) REFERENCES `users`(`id`) ON UPDATE CASCADE;
The Seeder function is bound after the Create function.
You can pass one param or multiple params as seeder data.
schema.Create("users", func(table interfaces.Blueprint) {
table.Id("id", 10)
table.String("username", 100)
table.String("password", 100)
table.Timestamps()
}).Seed([]map[string]interface{}{
{
"username": "admin",
"password": "1234",
},
{
"username": "user02",
"password": "1234",
},
}...)
The above will generate the following sql:
CREATE TABLE `users`(
`id` INT(10) NOT NULL AUTO_INCREMENT, PRIMARY KEY(`id`),
`username` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT NULL
);
INSERT INTO `users`(`password`, `username`) VALUES('1234', 'admin');
INSERT INTO `users`(`password`, `username`) VALUES('1234', 'user02');
Command
usesMigrator
to check migrations status and callsMigration
.Migration
useSchema
in Up() and Down().Schema
calledBlueprint
to get the blueprint and execute it to alter the database.Blueprint
defines how to generate columns.
You just need to focus on the Up() and Down() function, so you just need to understand that Blueprint
and Schema
.
You need to create a directory in the pkg/lib/
and implement the above function.