🎄

sqldef を Go のアプリケーションに組み込む

2022/12/18に公開約2,500字

これは Go Advent Calendar 2022 の 18 日目の記事です。

はじめに

sqldef は SQL で羃等に DB スキーマ管理ができるツールです。入力された DDL ファイルと実際の DB のスキーマを比較して必要な DDL を自動で生成・実行してくれます。

sqldef は CLI ツールとして提供されており、通常は DB に接続可能なサーバーにバイナリを配置して実行します。 sqldef をラップした npm ライブラリも提供されており、 Node.js のアプリケーションに組み込んで使うこともできます。また WebAssembly ライブラリも用意されています。

sqldef を Go のアプリケーションに組み込む

sqldef は Go で書かれています。また、一般的な Go ツールと同じように main パッケージと実装のパッケージが分離されています。そのため、公式の README などに記述はありませんが、ライブラリとして Go のアプリケーションに組み込むことができます。

実際に PostgreSQL で動作するコードを記載します。

import (
	"errors"
	"fmt"
	"log"
	"net/url"
	"os"
	"strconv"

	"github.com/k0kubun/sqldef"
	"github.com/k0kubun/sqldef/database"
	"github.com/k0kubun/sqldef/database/postgres"
	"github.com/k0kubun/sqldef/parser"
	"github.com/k0kubun/sqldef/schema"
)

func migrate(databaseURL, schemaFile string) error {
	u, err := url.Parse(databaseURL)
	if err != nil {
		return fmt.Errorf("failed to parse the database url: %w", err)
	}
	password, ok := u.User.Password()
	if !ok {
		return fmt.Errorf("failed to get password from the database url: %s", u.User.String())
	}
	port, err := strconv.Atoi(u.Port())
	if err != nil {
		if errors.Is(err, strconv.ErrSyntax) {
			port = 5432
		} else {
			return fmt.Errorf("failed to convert port in the database url: %w", err)
		}
	}
	db, err := postgres.NewDatabase(database.Config{
		DbName:   u.Path[1:],
		User:     u.User.Username(),
		Password: password,
		Host:     u.Hostname(),
		Port:     port,
	})
	if err != nil {
		return fmt.Errorf("failed to create a database adapter: %w", err)
	}
	sqlParser := database.NewParser(parser.ParserModePostgres)
	desiredDDLs, err := sqldef.ReadFile(schemaFile)
	if err != nil {
		return fmt.Errorf("Failed to read %s: %w", schemaFile, err)
	}
	options := &sqldef.Options{DesiredDDLs: desiredDDLs}
	if u.Hostname() == "localhost" {
		os.Setenv("PGSSLMODE", "disable")
	}
	sqldef.Run(schema.GeneratorModePostgres, db, sqlParser, options)
}

基本的には sqldef の PostgreSQL 向け CLI である psqldef の main パッケージと同じ形です。アプリケーション起動時にこの関数を呼び出すことでマイグレーションを行うことができます。

おわりに

小規模なシステムではありますが、このコードは実際の本番環境で運用しています。 sqldef 自体はまだメジャーバージョン 0 なのでまれに破壊的は変更が入ることがあり、その変更には追従する必要がありますが、それ以外では特に大きな問題もなく運用できています。

Discussion

ログインするとコメントできます