「GoCN酷Go推荐」sql 发布管理工具 gopg-migrations

1. gopg-migrations 是什么?

gopg-migrations 在我们使用数据库时,每次发布都要改一些表结构,或者修改数据之类的操作。

这些操作,手动执行既危险,又容易忘记;当新人看到奇怪的表结构,感觉到很诧异。如果我们能将所有操作管理起来,本地测试好之后再发布上去,那么就不会有上述这些问题。

因此,gopg-migrations 这个库,给我们带来非常大的便利。当然,这里是对 postgres 数据库的支持。

2. 怎么使用

下载扩展库 github.com/go-pg/migrations/v7即可使用。

2.1 假如我们先创建一个数据库 students,然后调用 migrations 去执行代码。

步骤如下:

编写创建表的 sql,使用 migrations 目录管理起来,num_xxx 表示按照 num 顺序执行 主逻辑 main.go 主逻辑如下:

package main

import (
    "fmt"
    "time"

    pg "github.com/test_go_pg/pg"

    "go.uber.org/zap"
)

func main() {
    fmt.Println("Starting go-pg-migrations...")

    // Bootstrap check pg
    if err := pg.PGDBWrite.Ping(); err != nil {
        fmt.Println(pg.DBWriteConnectionError, zap.Error(err))
        return
    }
    fmt.Println("PostgreSQL is running",
        zap.String("user", pg.PGDBWrite.Options().User),
        zap.String("addr", pg.PGDBWrite.Options().Addr),
        zap.String("db", pg.PGDBWrite.Options().Database))

    // Migrate to latest pg schema
    if err := pg.PGDBWrite.Migrate(); err != nil {
        fmt.Println(pg.DBMigrationError, zap.Error(err))
        return
    }

    time.Sleep(time.Hour)
    fmt.Println("go-pg-migrations is stopping...")
}

gopg-migrations 相关函数编码如下:

package pg

import (
    "context"
    "fmt"
    "time"

    "github.com/go-pg/migrations/v7"
    "github.com/go-pg/pg/v9"
    "go.uber.org/zap"
)

type TGPGDB struct {
    *pg.DB
}

func NewTGPGDB(db *pg.DB) *TGPGDB {
    return &TGPGDB{db}
}

type TGPGDBOptions struct {
    Url string

    DisableBeforeQueryLog bool
    DisableAfterQueryLog  bool
}

func CreateTGPGDB(url string) *TGPGDB {
    return CreateTGPGDBWithOptions(&TGPGDBOptions{Url: url})
}

func CreateTGPGDBWithOptions(dbOpts *TGPGDBOptions) *TGPGDB {
    opts, err := pg.ParseURL(dbOpts.Url)
    if err != nil {
        fmt.Println(DBURLParseError, zap.String("URL", dbOpts.Url), zap.Error(err))
        return nil
    }
    opts.ReadTimeout = DBReadTimeout
    opts.WriteTimeout = DBWriteTimeout
    opts.TLSConfig = nil // disabled for faster local connection (even in production)
    if DBNumConns > 0 {
        opts.PoolSize = DBNumConns
    }

    db := NewTGPGDB(pg.Connect(opts))
    return db
}

// Ping simulates a "blank query" behavior similar to lib/pq's
// to check if the db connection is alive.
func (db *TGPGDB) Ping() error {
    _, err := db.ExecOne("SELECT 1")
    return err
}

// Migrate check and migrate to lastest db version.
func (db *TGPGDB) Migrate() error {
    // Make sure to only search specified migrations dir
    cl := migrations.NewCollection()
    cl.DisableSQLAutodiscover(true)
    err := cl.DiscoverSQLMigrations(DBMigrationsDir)
    if err != nil {
        return err
    }

    var oldVersion, newVersion int64
    // Run all migrations in a transaction so we rollback if migrations fail anywhere
    err = db.RunInTransaction(func(tx *pg.Tx) error {
        // Intentionally ignore harmless errors on initializing gopg_migrations
        _, _, err = cl.Run(db, "init")
        //if err != nil && !DBMigrationsAlreadyInit(err) {
        if err != nil{
            return err
        }
        oldVersion, newVersion, err = cl.Run(db, "up")
        return err
    })
    if err != nil {
        return err
    }
    if newVersion == oldVersion {
        fmt.Println("db schema up to date")
    } else {
        fmt.Println("db schema migrated successfully", zap.Int64("from", oldVersion), zap.Int64("to", newVersion))
    }
    return nil
}

// WithContextTimeout
func WithContextTimeout(ctx context.Context, f func(ctx context.Context)) {
    WithContextTimeoutValue(ctx, DBStmtTimeout, f)
}

// WithContextTimeoutValue
func WithContextTimeoutValue(ctx context.Context, timeout time.Duration, f func(ctx context.Context)) {
    // check context timeout setting with upper bound read/write limit
    if timeout > DBReadTimeout && timeout > DBWriteTimeout {
        fmt.Println(DBContextTimeoutExceedUpperBound,
            zap.Error(fmt.Errorf("query timeout %s exceed upper bound (%s|%s)", timeout, DBReadTimeout, DBWriteTimeout)))
    }
    newCtx, cancel := context.WithTimeout(ctx, timeout)
    f(newCtx)
    cancel()
}

直接运行 go run main.go,此时代码运行,会生成 students 数据表:

sql 如下:

bd3bf20d8d4505da09e258bfcbc3974d.png

执行后,会创建号数据表:students 和 gopg-migrations。

113dc6fca550f1e21c929915fe049ee4.png

查看 gopg-migrations 如下:

55042237be45319dbe5c0eec2d96cfcd.png

2.2 假设我们要增加 sql 语句,怎么做呢?

直接添加 sql 重新运行 sql 如下:

58f1ec2716e2d6248ecfc1592fca09ae.png

执行如下:

f86a972407d0af442cbaed1ffe1ed636.png

查看 students 数据表,发现 1,2 操作都已经执行了:

ad4a7e2fb3db19eb3f79b4b7289ec17f.png

2.3.管理 sql 语句

类似 1,2 中的方式,我们可以添加更多的 sql 语句,把 sql 管理起来。

总结

gopg-migrations 这个库,能够把数据库所有的操作 sql 全部管理起来,一方面可以让我们清晰的看到 sql 项目的发展历程,另一方面本地测试后发布更加安全!

以上所有内容均采用最新官方案例做示例

参考资料

查看全部代码 (https://github.com/turingczz/test_go_pg)

图片上传及查看,使用 swarm 网关实现(https://swarm-gateways.net/)

《酷Go推荐》招募:

各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到

新的库,并且知道怎么用。

大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!戳「阅读原文」,即可报名

扫码也可以加入 GoCN 的大家族哟~

f9fbe903619920912166342c796b2c99.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值