Chapter 03

PostgreSQL に接続する

Spiegel
Spiegel
2022.04.03に更新

では実際に PostgreSQL サービスに接続してみよう。

Go の標準パッケージである database/sql を使って RDBMS に接続するには DB の種別ごとに「ドライバ」と呼ばれるパッケージをインポートする必要がある。 PostgreSQL の場合は github.com/lib/pq が定番として使われることが多かった。

proto/sample2.go
//go:build run
// +build run

package main

import (
    "database/sql"
    "log"
    "os"

    "github.com/joho/godotenv"
    _ "github.com/lib/pq"
    "github.com/goark/gocli/config"
    "github.com/goark/gocli/exitcode"
)

func init() {
    //load ~/.config/elephantsql/env file
    if err := godotenv.Load(config.Path("elephantsql", "env")); err != nil {
        panic(err)
    }
}

func Run() exitcode.ExitCode {
    // create sql.DB instance for PostgreSQL service
    db, err := sql.Open("postgres", os.Getenv("ELEPHANTSQL_URL"))
    if err != nil {
        log.Println(err)
        return exitcode.Abnormal
    }
    defer db.Close()

    return exitcode.Normal
}

func main() {
    Run().Exit()
}

ただ,最近は github.com/jackc/pgx パッケージのほうが定番になりつつあるようだ(以下 import と Run() 関数のみ挙げておく)。

proto/sample2b.go
import (
    "database/sql"
    "log"
    "os"

    _ "github.com/jackc/pgx/v4/stdlib"
    "github.com/joho/godotenv"
    "github.com/goark/gocli/config"
    "github.com/goark/gocli/exitcode"
)

func Run() exitcode.ExitCode {
    // create sql.DB instance for PostgreSQL service
    db, err := sql.Open("pgx", os.Getenv("ELEPHANTSQL_URL"))
    if err != nil {
        log.Println(err)
        return exitcode.Abnormal
    }
    defer db.Close()

    return exitcode.Normal
}

database/sql を使わず github.com/jackc/pgx パッケージを直に使って接続することもできる。

proto/sample3.go
import (
    "context"
    "log"
    "os"

    "github.com/jackc/pgx/v4/pgxpool"
    "github.com/joho/godotenv"
    "github.com/goark/gocli/config"
    "github.com/goark/gocli/exitcode"
)

func Run() exitcode.ExitCode {
    // create connection pool for PostgreSQL service
    pool, err := pgxpool.Connect(context.TODO(), os.Getenv("ELEPHANTSQL_URL"))
    if err != nil {
        log.Println(err)
        return exitcode.Abnormal
    }
    defer pool.Close()

    return exitcode.Normal
}

pgx.Connect() 関数ではなく pgxpool.Connect() 関数を使って接続している点に注意。 pgx.Connect() 関数で得られる pgx.Conn インスタンスは単一接続を表し並行的に安全(concurrency safe)ではないそうな。

`*pgx.Conn` represents a single connection to the database and is not concurrency safe. Use sub-package pgxpool for a concurrency safe connection pool.
(via “pgx package - github.com/jackc/pgx/v4 - pkg.go.dev”)

pgxpool.Connect() 関数の引数に context.Context インスタンスが渡されている点にも注意。 github.com/jackc/pgx パッケージではほとんどの処理で context.Context インスタンスを渡すようになっている。今時ですな。

github.com/jackc/pgx パッケージ自体はかなり高機能なのだが,世の中にある Go 用の ORM (Object Relational Mapper) フレームワークの大抵は database/sql 標準パッケージを前提にしているのが辛いところである。