pressly/goose

GitHub: pressly/goose

一个支持 SQL 与 Go 函数的数据库迁移工具,用于安全、可追溯地管理数据库架构变更。

Stars: 10514 | Forks: 638

# goose [![Goose CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/063b3cc5b3040532.svg)](https://github.com/pressly/goose/actions/workflows/ci.yaml) [![Go Reference](https://pkg.go.dev/badge/github.com/pressly/goose/v3.svg)](https://pkg.go.dev/github.com/pressly/goose/v3) [![Go Report Card](https://goreportcard.com/badge/github.com/pressly/goose/v3)](https://goreportcard.com/report/github.com/pressly/goose/v3) Goose 是一个数据库迁移工具。它既支持命令行也支持库的形式使用。 通过创建增量的 SQL 变更或 Go 函数来管理你的 **数据库架构(schema)**。 #### 功能特性 - 支持多种数据库: - PostgreSQL、MySQL、Spanner、SQLite、YDB、ClickHouse、MSSQL、Vertica,以及 更多。 - 支持以普通函数形式编写 Go 迁移。 - 支持 [嵌入式](https://pkg.go.dev/embed/) 迁移。 - 无序迁移。 - 数据填充(Seeding)。 - 在 SQL 迁移中支持环境变量替换。 - ……还有更多。 # 安装 ``` go install github.com/pressly/goose/v3/cmd/goose@latest ``` 这会将 `goose` 二进制文件安装到你的 `$GOPATH/bin` 目录。 二进制文件过大?可以通过排除不需要的数据库驱动来构建精简版本: ``` go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose # Available build tags: # no_clickhouse no_libsql no_mssql no_mysql # no_postgres no_sqlite3 no_vertica no_ydb ``` macOS 用户可以通过 [Homebrew Formulae](https://formulae.brew.sh/formula/goose#default) 安装 `goose`: ``` brew install goose ``` 更多细节请参考 [安装文档](https://pressly.github.io/goose/installation/)。 # 用法
点击显示 goose help 输出 ``` Usage: goose DRIVER DBSTRING [OPTIONS] COMMAND or Set environment key GOOSE_DRIVER=DRIVER GOOSE_DBSTRING=DBSTRING GOOSE_MIGRATION_DIR=MIGRATION_DIR Usage: goose [OPTIONS] COMMAND Drivers: postgres mysql sqlite3 spanner mssql redshift tidb clickhouse ydb starrocks turso Examples: goose sqlite3 ./foo.db status goose sqlite3 ./foo.db create init sql goose sqlite3 ./foo.db create add_some_column sql goose sqlite3 ./foo.db create fetch_user_data go goose sqlite3 ./foo.db up goose postgres "user=postgres dbname=postgres sslmode=disable" status goose mysql "user:password@/dbname?parseTime=true" status goose spanner "projects/project/instances/instance/databases/database" status goose redshift "postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" status goose tidb "user:password@/dbname?parseTime=true" status goose mssql "sqlserver://user:password@hostname:1433?database=master" status goose clickhouse "tcp://127.0.0.1:9000" status goose ydb "grpcs://localhost:2135/local?go_query_mode=scripting&go_fake_tx=scripting&go_query_bind=declare,numeric" status goose starrocks "user:password@/dbname?parseTime=true&interpolateParams=true" status GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose status GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./foo.db goose create init sql GOOSE_DRIVER=postgres GOOSE_DBSTRING="user=postgres dbname=postgres sslmode=disable" goose status GOOSE_DRIVER=mysql GOOSE_DBSTRING="user:password@/dbname" goose status GOOSE_DRIVER=redshift GOOSE_DBSTRING="postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439/db" goose status GOOSE_DRIVER=clickhouse GOOSE_DBSTRING="clickhouse://user:password@qwerty.clickhouse.cloud:9440/dbname?secure=true&skip_verify=false" goose status Options: -allow-missing applies missing (out-of-order) migrations -certfile string file path to root CA's certificates in pem format (only support on mysql) -dir string directory with migration files (default ".", can be set via the GOOSE_MIGRATION_DIR env variable). -h print help -no-color disable color output (NO_COLOR env variable supported) -no-versioning apply migration commands with no versioning, in file order, from directory pointed to -s use sequential numbering for new migrations -ssl-cert string file path to SSL certificates in pem format (only support on mysql) -ssl-key string file path to SSL key in pem format (only support on mysql) -table string migrations table name (default "goose_db_version"). If you use a schema that is not `public`, you should set `schemaname.goose_db_version` when running commands. -timeout duration maximum allowed duration for queries to run; e.g., 1h13m -v enable verbose mode -version print version Commands: up Migrate the DB to the most recent version available up-by-one Migrate the DB up by 1 up-to VERSION Migrate the DB to a specific VERSION down Roll back the version by 1 down-to VERSION Roll back to a specific VERSION redo Re-run the latest migration reset Roll back all migrations status Dump the migration status for the current DB version Print the current version of the database create NAME [sql|go] Creates new migration file with the current timestamp fix Apply sequential ordering to migrations validate Check migration files without running them ```
常用命令: [create](#create) •  [up](#up) •  [up-to](#up-to) •  [down](#down) •  [down-to](#down-to) •  [status](#status) •  [version](#version) ## create 创建新的 SQL 迁移文件。 ``` $ goose create add_some_column sql $ Created new file: 20170506082420_add_some_column.sql $ goose -s create add_some_column sql $ Created new file: 00001_add_some_column.sql ``` 编辑新创建的文件以定义迁移行为。 你也可以创建一个 Go 迁移,如果随后使用你自己的 goose 二进制文件调用它: ``` $ goose create fetch_user_data go $ Created new file: 20170506082421_fetch_user_data.go ``` ## up 应用所有可用的迁移。 ``` $ goose up $ OK 001_basics.sql $ OK 002_next.sql $ OK 003_and_again.go ``` ## up-to 迁移到指定版本。 ``` $ goose up-to 20170506082420 $ OK 20170506082420_create_table.sql ``` ## up-by-one 从当前版本迁移一步。 ``` $ goose up-by-one $ OK 20170614145246_change_type.sql ``` ## down 回滚当前版本的一条迁移。 ``` $ goose down $ OK 003_and_again.go ``` ## down-to 回滚到指定版本。 ``` $ goose down-to 20170506082527 $ OK 20170506082527_alter_column.sql ``` 或者,回滚所有迁移(请小心!): ``` $ goose down-to 0 ``` ## status 打印所有迁移的状态: ``` $ goose status $ Applied At Migration $ ======================================= $ Sun Jan 6 11:25:03 2013 -- 001_basics.sql $ Sun Jan 6 11:25:03 2013 -- 002_next.sql $ Pending -- 003_and_again.go ``` 注意:对于 MySQL,需要启用 [parseTime 标志](https://github.com/go-sql-driver/mysql#parsetime)。 注意:对于 MySQL [`multiStatements`](https://github.com/go-sql-driver/mysql?tab=readme-ov-file#multistatements) 必须 启用。当在单个 sql 文件中使用分号分隔多条查询时,这是必需的。 ## version 打印当前数据库版本: ``` $ goose version $ goose: version 002 ``` # 环境变量 如果你更倾向于使用环境变量,而不是将驱动和数据库字符串作为参数传递,可以设置以下环境变量: **1. 通过环境变量:** ``` export GOOSE_DRIVER=DRIVER export GOOSE_DBSTRING=DBSTRING export GOOSE_MIGRATION_DIR=MIGRATION_DIR export GOOSE_TABLE=TABLENAME ``` **2. 通过 `.env` 文件及对应变量。`.env` 文件示例**: ``` GOOSE_DRIVER=postgres GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/admin_db GOOSE_MIGRATION_DIR=./migrations GOOSE_TABLE=custom.goose_migrations ``` 默认启用从 `.env` 文件加载。若要禁用此功能,请设置 `-env=none` 标志。 如果要从特定文件加载,请设置 `-env` 标志为文件路径。 有关环境变量的更多细节,请参考[官方文档](https://pressly.github.io/goose/documentation/environment-variables/)。 # 迁移 goose 支持 SQL 或 Go 编写的迁移。 ## SQL 迁移 一个示例 SQL 迁移如下: ``` -- +goose Up CREATE TABLE post ( id int NOT NULL, title text, body text, PRIMARY KEY(id) ); -- +goose Down DROP TABLE post; ``` 每个迁移文件必须恰好包含一个 `-- +goose Up` 注释。`-- +goose Down` 注释是可选的。如果文件同时包含两者,`-- +goose Up` 注释 **必须** 排在前面。 请注意注释中的标记。`-- +goose Up` 之后的语句将在正向迁移中执行,`-- +goose Down` 之后的语句将在回滚中执行。 默认情况下,所有迁移都在事务中运行。但像 `CREATE DATABASE` 这样的语句无法在事务中运行。你可以选择在迁移文件顶部添加 `-- +goose NO TRANSACTION` 来跳过该文件的事务。 此文件中的正向和回滚迁移都将无事务运行。 默认情况下,SQL 语句以分号分隔——实际上,查询语句必须以分号结尾才能被 goose 正确识别。 默认情况下,所有迁移都在 public 模式下运行。如果要使用其他模式,请使用表选项指定模式名称,例如 `-table='schemaname.goose_db_version`。 包含分号的复杂语句(如 PL/pgSQL)必须用 `-- +goose StatementBegin` 和 `-- +goose StatementEnd` 注释包裹,以便正确识别。例如: ``` -- +goose Up -- +goose StatementBegin CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE ) returns void AS $$ DECLARE create_query text; BEGIN FOR create_query IN SELECT 'CREATE TABLE IF NOT EXISTS histories_' || TO_CHAR( d, 'YYYY_MM' ) || ' ( CHECK( created_at >= timestamp ''' || TO_CHAR( d, 'YYYY-MM-DD 00:00:00' ) || ''' AND created_at < timestamp ''' || TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' ) || ''' ) ) inherits ( histories );' FROM generate_series( $1, $2, '1 month' ) AS d LOOP EXECUTE create_query; END LOOP; -- LOOP END END; -- FUNCTION END $$ language plpgsql; -- +goose StatementEnd ``` goose 支持在 SQL 迁移中通过注释进行环境变量替换。要启用此功能,请在希望应用替换的查询前使用 `-- +goose ENVSUB ON` 注释。 它会保持激活状态,直到遇到 `-- +goose ENVSUB OFF` 注释。 你可以在一个文件中多次使用这些注释。 此功能默认因向后兼容性而禁用。 对于 `PL/pgSQL` 函数或其他不需要替换的语句,请显式地将相关部分用注释包裹。例如,要排除对 `$$` 字符的转义: ``` -- +goose StatementBegin CREATE OR REPLACE FUNCTION test_func() RETURNS void AS $$ -- +goose ENVSUB ON BEGIN RAISE NOTICE '${SOME_ENV_VAR}'; END; -- +goose ENVSUB OFF $$ LANGUAGE plpgsql; -- +goose StatementEnd ```
点击展开支持的变量(点击此处展开): - `${VAR}` 或 `$VAR` - 展开为环境变量 `VAR` 的值 - `${VAR:-default}` - 展开为环境变量 `VAR` 的值,若 `VAR` 未设置或为 null,则为 `default` - `${VAR-default}` - 展开为环境变量 `VAR` 的值,若 `VAR` 未设置则为 `default` - `${VAR?err_msg}` - 展开为环境变量 `VAR` 的值,若 `VAR` 未设置则打印 `err_msg` 并报错 - ~~`${VAR:?err_msg}` - 展开为环境变量 `VAR` 的值,或打印 `err_msg` 并报错(若 `VAR` 未设置或为 null)。~~ **此功能不支持** 更多细节请参考 [mfridman/interpolate](https://github.com/mfridman/interpolate?tab=readme-ov-file#supported-expansions)
## 嵌入式 SQL 迁移 Go 1.16 引入了新特性:[编译时嵌入](https://pkg.go.dev/embed/) 文件到二进制中,以及对应的 [文件系统抽象](https://pkg.go.dev/io/fs/)。 此特性仅适用于应用现有迁移。修改操作(如 `fix` 和 `create`)将继续在操作系统文件系统上运行,即使使用了嵌入式文件。 这是预期行为,因为 `io/fs` 接口只允许只读访问。 请确保配置正确的 SQL 方言,可参考 [dialect.go](./dialect.go) 支持的 SQL 方言。 示例用法,假设 SQL 迁移文件位于 `migrations` 目录: ``` package main import ( "database/sql" "embed" "github.com/pressly/goose/v3" ) //go:embed migrations/*.sql var embedMigrations embed.FS func main() { var db *sql.DB // setup database goose.SetBaseFS(embedMigrations) if err := goose.SetDialect("postgres"); err != nil { panic(err) } if err := goose.Up(db, "migrations"); err != nil { panic(err) } // run app } ``` 注意我们将 `"migrations"` 作为 `Up` 的目录参数,因为嵌入会保存目录结构。 ## Go 迁移 1. 创建你自己的 goose 二进制文件,参考 [示例](./examples/go-migrations) 2. 导入 `github.com/pressly/goose` 3. 注册你的迁移函数 4. 将你的 `migrations` 包包含在 Go 构建中:在 `main.go` 中使用 `import _ "github.com/me/myapp/migrations"` 5. 运行 goose 命令,例如 `goose.Up(db *sql.DB, dir string)` 一个 [示例 Go 迁移文件 0002_users_add_email.go](./examples/go-migrations/00002_rename_root.go) 看起来像这样: ``` package migrations import ( "database/sql" "github.com/pressly/goose/v3" ) func init() { goose.AddMigration(Up, Down) } func Up(tx *sql.Tx) error { _, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';") if err != nil { return err } return nil } func Down(tx *sql.Tx) error { _, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';") if err != nil { return err } return nil } ``` 请注意,Go 迁移文件必须以数字开头,后跟下划线,且不能以 `*_test.go` 结尾。 # 混合版本控制 请先阅读 [版本问题](https://github.com/pressly/goose/issues/63#issuecomment-428681694)。 默认情况下,如果尝试应用缺失的(无序)迁移,goose 会报错。 但如果你希望应用这些缺失的迁移,可以给 goose 加上 `-allow-missing` 标志,或者在使用库时提供函数选项 `goose.WithAllowMissing()` 给 Up、UpTo 或 UpByOne。 不过,我们强烈建议采用混合版本控制方法,结合使用时间戳和顺序编号。在开发过程中创建的迁移使用时间戳,而在生产环境中运行的则使用顺序版本。我们相信这种方法能防止团队开发时版本冲突的问题。 为了帮助你采用这种方法,`create` 将使用当前时间戳作为迁移版本。 当你准备在生产环境中部署迁移时,我们还提供了一个有用的 `fix` 命令,可以将迁移转换为顺序排列,同时保留时间戳顺序。我们建议在 CI 流水线中运行 `fix`,并且仅在迁移准备生产部署时运行。 ## 致谢 Gopher 吉祥物由 [Renée French](https://reneefrench.blogspot.com/) 设计 / [CC 3.0.](https://creativecommons.org/licenses/by/3.0/)。更多信息请参考 [Go 博客](https://go.dev/blog/gopher)。由 Ellen 改编。 ## 许可证 根据 [MIT 许可证](./LICENSE) 授权。
标签:CI, ClickHouse, EVTX分析, Goose, GoReport, Go函数迁移, Go语言, Go迁移函数, Homebrew, out-of-order迁移, PostgreSQL, Spanner, SQLite, SQL迁移, Vertica, YDB, 增量迁移, 多数据库支持, 多线程, 嵌入迁移, 库迁移, 数据库工具, 数据库迁移, 数据种子, 文档结构分析, 日志审计, 构建标签, 模式迁移, 环境变量替换, 程序破解