PostgreSQLのDDLをGoから取得したい
PostgreSQLには SHOW CREATE TABLE
のようなDDLを表示するステートメントがない。
代替として、How to generate the "create table" sql statement for an existing table in postgreSQL - Stack Overflow には2通りの方法が挙げられている。
- コマンドラインから
pg_dump
で出力する -
pg_catalog
内のテーブルを組み合わせた関数を作成して出力する
2つ目の方法はSQLで取得できるお手軽さがあるが、複雑なSQLを使用しているのでメンテが面倒。
1つ目の方法をGolangで実現する方法を考えていきたい。
以下のライブラリが参考になりそう。
ライブラリの中でどのように pg_dump
を実行しているか見てみたが、単に実行環境の pg_dump
をコマンドで実行しているだけだった。
pg_dump
自体をライブラリ化しているGoのパッケージがないか探してみたが、今日(2022年5月6日)時点では存在していない。
pg_dump
はC言語で書かれているので、GoからCの関数を実行できれば事前に pg_dump
をインストールしておく必要もなくなりそう。(多分?)
- PostgreSQL Source Code: src/bin/pg_dump/pg_dump.c File Reference
- GoからCのライブラリを呼ぶ - Qiita
- CGo - Referencing C library in Go - Blog
個人的に未知の領域で時間がかかりそうなので、今回は habx/pg-commands
と同じ手法を取る。
ちなみに pg_dump
は内部でSELECT文を発行している模様。
PostgreSQL: Documentation: 14: pg_dump
pg_dump internally executes SELECT statements.
これの中身が分かれば簡単に再現できそうだが、情報が見つからなかった…。
結果、思いのほか単純な関数になった。
executePgDump
は Connection String
と特定のスキーマ名・テーブル名を引数に取る。
DB的にはテーブルを指定しない方がコール回数を抑えられるが、指定することで出力されるテキスト内の CREATE
文が1つに抑えられる(はず?)。こちらの方が、正規表現がシンプルになって良い。
package main
import (
"bytes"
"fmt"
"os/exec"
"regexp"
)
func setConStr(user string, pass string, host string, port string, db string) string {
return fmt.Sprintf("sslmode=require user=%s password=%s host=%s port=%s dbname=%s connect_timeout=300", user, pass, host, port, db)
}
func executePgDump(conStr string, schema string, table string) (string, error) {
t := fmt.Sprintf("\"%s\".\"%s\"", schema, table)
args := []string{
"-t", t,
"--schema-only",
"--quote-all-identifiers",
"-d", conStr,
}
cmd := exec.Command("pg_dump", args...)
out := bytes.Buffer{}
stderr := bytes.Buffer{}
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
return "", err
}
ddl := parseDdl(out.Bytes())
return ddl, nil
}
func parseDdl(dump []byte) string {
r := regexp.MustCompile("(?mi)create.* (table|view|materialized view|)[^;]*;")
ddl := r.Find(dump)
return string(ddl)
}