Contents

Go goose

Goose

数据库版本管理

Goose 是一个用于管理数据库迁移的工具,类似于 Flyway 和 Liquibase。它可以方便地管理数据库模式的版本,并应用相应的 SQL 脚本。你提到在 db/migrations/ 目录下有多个 SQL 文件,这些文件通常是用来管理数据库迁移的。

一、基本使用

1、安装

go install github.com/pressly/goose/v3/cmd/goose@latest

对于不希望安装与数据库连接相关命令的轻量版,可以使用独有的构建标签:

go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose

2、使用方法

以下表格说明了 Goose 工具的基本用法:

命令 描述
up 将数据库迁移到最新的版本
up-to 迁移到特定版本
up-by-one 从当前版本向上迁移一个版本
down 回滚当前版本的前一个版本
down-to 回滚到特定版本
redo 回滚最新应用的迁移然后重新应用
status 输出当前数据库的迁移状态
version 输出数据库当前的版本
create 创建新的迁移文件,可以选择 sqlgo 格式
fix 应用序列编号到迁移
validate 检查迁移文件,而不实际运行

你可以设置环境变量 GOOSE_DRIVER, GOOSE_DBSTRING, 和 GOOSE_MIGRATION_DIR 来定制 Goose 的行为。

Goose支持的数据库类型

  • mssql,sqlserver,mysql,tidb,sqlite3,clickhouse

具体看 goose.OpenDBWithDriver() 方法

二、集成

package main

import (
	"context"
	"flag"
	"log"
	"os"

	"github.com/pressly/goose/v3"
	_ "modernc.org/sqlite"
)

var (
	flags = flag.NewFlagSet("goose", flag.ExitOnError)
	dir   = flags.String("dir", ".", "directory with migration files")
)

func main() {
	flags.Parse(os.Args[1:])
	args := flags.Args()

	if len(args) < 3 {
		flags.Usage()
		return
	}

	dbstring, command := args[1], args[2]

	db, err := goose.OpenDBWithDriver("sqlite", dbstring)
	if err != nil {
		log.Fatalf("goose: failed to open DB: %v\n", err)
	}

	defer func() {
		if err := db.Close(); err != nil {
			log.Fatalf("goose: failed to close DB: %v\n", err)
		}
	}()

	arguments := []string{}
	if len(args) > 3 {
		arguments = append(arguments, args[3:]...)
	}

	if err := goose.RunContext(context.Background(), command, db, *dir, arguments...); err != nil {
		log.Fatalf("goose %v: %v", command, err)
	}
}

使用

[root@Sugar goose]🐳 ./cccs sqlite3 ./foo.db status
2024/11/24 15:28:17 goose status: failed to collect migrations: no migration files found
[root@Sugar goose]🐳 ./cccs sqlite3 ./foo.db status -dir dbFile
2024/11/24 15:28:32 goose status: failed to collect migrations: no migration files found
[root@Sugar goose]🐳 ./cccs -dir dbFile sqlite3 ./foo.db status 
2024/11/24 15:28:51     Applied At                  Migration
2024/11/24 15:28:51     =======================================
2024/11/24 15:28:51     Pending                  -- 20241124142616_v24_2_1_add_some_column.sql
[root@Sugar goose]🐳 ./cccs -dir dbFile sqlite3 ./foo.db up 
2024/11/24 15:29:05 OK   202411xxxx2616_v24_2_1_add_some_column.sql (2.57ms)
2024/11/24 15:29:05 goose: successfully migrated database to version: 20241124142616
[root@Sugar goose]🐳 ./cccs -dir dbFile sqlite3 ./foo.db down
2024/11/24 15:31:04 OK   20241124142616_v24_2_1_add_some_column.sql (2.96ms)
[root@Sugar goose]🐳 ./cccs -dir dbFile sqlite3 ./foo.db up 
2024/11/24 15:32:08 OK   20241124142616_v24_2_1_add_some_column.sql (2.77ms)
2024/11/24 15:32:08 goose: successfully migrated database to version: 20241124142616
[root@Sugar goose]🐳 

sql 文件打包到二进制中

//go:embed docs/sql/*.sql
var Migrations embed.FS


func GenTable(migrations embed.FS, gooseArgs []string, dir string) {
	goose.SetBaseFS(migrations)
	command := gooseArgs[0]
	gooseDB, err := goose.OpenDBWithDriver(config.Cfg.Database.Type, config.Cfg.Database.DBname)
	if err != nil {
		slog.Error("goose: failed to open DB", "err", err)
	}
	defer func() {
		if err := gooseDB.Close(); err != nil {
			slog.Error("goose: failed to close DB", "err", err)
		}
	}()
	arguments := []string{}
	if len(gooseArgs) > 1 {
		arguments = append(arguments, gooseArgs[1:]...)
	}

	if err := goose.RunContext(context.Background(), command, gooseDB, dir, arguments...); err != nil {
		slog.Error("goose: failed to run cmd", "cmd", command, "err", err)
	}
}