Open5

sqlc メモ

pgx を使う場合

  • sqlc.yaml で sql_package: "pgx/v4" を指定する

gin で利用する場合

SQL 側に @abc::bigint や @xyz::string のような形で好きな文字列を入れ込める。

WHERE now() + interval @interval::string とかができる。

その場合は $1 などは利用できなくなりかならず @ を利用する必要がある。@ は sqlc.args() の省略形。

https://docs.sqlc.dev/en/latest/howto/named_parameters.html

sqlc + dockertest + timescaledb

  • sqlc を利用したテストを書きたい
  • dockertest を利用して timescaledb でテストしたい
    • mock は使いたくない
  • テスト起動まで 10 秒程度かかる

サンプルコード

dockertest で timescaledb を起こして、schema.sql 読み込ませて ping 飛ばして疎通確認してる。

package spam

import (
	"context"
	"fmt"
	"log"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/ory/dockertest/v3"
	"github.com/ory/dockertest/v3/docker"
        // アプリ依存なので、変更が必要
	db "example.com/spam/db/sqlc"
)

var query *db.Queries

func TestMain(m *testing.M) {
	pool, err := dockertest.NewPool("")
	if err != nil {
		log.Fatalf("Could not connect to docker: %s", err)
	}

	pwd, _ := os.Getwd()

	runOptions := &dockertest.RunOptions{
		Repository: "timescale/timescaledb",
		Tag:        "2.5.0-pg14",
		Env: []string{
			"POSTGRES_USER=postgres",
			"POSTGRES_PASSWORD=password",
			"POSTGRES_DB=defaultdb",
			"listen_addresses='*'",
		},
		Mounts: []string{
			// scripts 以下にアプリがある前提 sqlc.yaml 参照
			pwd + "/scripts/schema/schema.sql:/docker-entrypoint-initdb.d/schema.sql",
		},
	}

	resource, err := pool.RunWithOptions(runOptions, func(config *docker.HostConfig) {
		config.AutoRemove = true
		config.RestartPolicy = docker.RestartPolicy{Name: "no"}
	})
	if err != nil {
		log.Fatalf("Could not start resource: %s", err)
	}

	hostAndPort := resource.GetHostPort("5432/tcp")
	databaseUrl := fmt.Sprintf("postgres://postgres:password@%s/defaultdb?sslmode=disable", hostAndPort)

	if err := pool.Retry(func() error {
		config, err := pgxpool.ParseConfig(databaseUrl)
		if err != nil {
			return err
		}

		p, err := pgxpool.ConnectConfig(context.Background(), config)
		if err != nil {
			return err
		}

		if err := p.Ping(context.Background()); err != nil {
			return err
		}

		query = db.New(p)
		return nil
	}); err != nil {
		log.Fatalf("Could not connect to database: %s", err)
	}

	code := m.Run()

	if err := pool.Purge(resource); err != nil {
		log.Fatalf("Could not purge resource: %s", err)
	}

	os.Exit(code)
}

func TestSpam(t *testing.T) {
	c := context.Background()
	spam, err := query.CreateSpam(c, "spam!spam!spam!")
	assert.Nil(t, err)
	assert.Equal(t, spam.Message, "spam!spam!spam!")
}

sqlc.yaml

難しいことはしていない。

version: "1"
packages:
  - name: "db"
    path: "./db/sqlc"
    queries: "scripts/query/"
    schema: "scripts/schema/"
    engine: "postgresql"
    sql_package: "pgx/v4"
    emit_json_tags: true
    emit_prepared_queries: false
    emit_interface: true
    emit_exact_table_names: false
    emit_empty_slices: true
作成者以外のコメントは許可されていません