pgx/v5でstring配列をやりとりする

github.com/jackc/pgx/v5 v5.7.6 での検証

pgx.Conn

pgx.Connを使っている場合は、[]stringをそのまま使えるのであまり考える必要はない。

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/jackc/pgx/v5"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, "postgres://postgres@localhost:5432/postgres")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close(ctx)

    // CREATE TABLE users (
    //  id    SERIAL PRIMARY KEY,
    //  name  VARCHAR(100),
    //  email TEXT,
    //  tags  VARCHAR(50)[]
    // );

    var name string
    var tags []string

    emails := []string{"sugawara@example.com", "hoge@example.com"}
    err = conn.QueryRow(ctx, "SELECT name, tags FROM users WHERE email = ANY($1)", emails).Scan(&name, &tags)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(name, tags) //=> sugawara [foo bar zoo]
}

stdlib

プレースホルダーにstring配列を渡す

プレースホルダーには[]stringをそのまま渡せる。

package main

import (
    "database/sql"
    "fmt"
    "log"

    _ "github.com/jackc/pgx/v5/stdlib"
)

func main() {
    db, err := sql.Open("pgx", "postgres://postgres@localhost:5432/postgres")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // CREATE TABLE users (
    //  id    SERIAL PRIMARY KEY,
    //  name  VARCHAR(100),
    //  email TEXT,
    //  tags  VARCHAR(50)[]
    // );

    var name string

    emails := []string{"sugawara@example.com", "hoge@example.com"}
    err = db.QueryRow("SELECT name FROM users WHERE email = ANY($1)", emails).Scan(&name)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(name) //=> sugawara
}

結果のstring配列を受け取る

string配列を受け取る場合はpgtype.NewMapを使う必要がある。

package main

import (
    "database/sql"
    "fmt"
    "log"

    "github.com/jackc/pgx/v5/pgtype"
    _ "github.com/jackc/pgx/v5/stdlib"
  // "github.com/lib/pq"
)

func main() {
    db, err := sql.Open("pgx", "postgres://postgres@localhost:5432/postgres")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // CREATE TABLE users (
    //  id    SERIAL PRIMARY KEY,
    //  name  VARCHAR(100),
    //  email TEXT,
    //  tags  VARCHAR(50)[]
    // );

    var tags []string

    err = db.QueryRow("SELECT tags FROM users WHERE name = $1", "sugawara").Scan(pgtype.NewMap().SQLScanner(&tags))
    // あるいは pq.Arrayを使う https://pkg.go.dev/github.com/lib/pq#Array
    //err = db.QueryRow("SELECT tags FROM users WHERE name = $1", "sugawara").Scan(pq.Array(&tags))

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(tags) //=> [foo bar zoo]
}

以下のIssueではpgtype.FlatArrayを使っているが不要なようだった。

github.com

https://github.com/jackc/pgx/blob/61d3c965ad442cc14d6b0e39e0ab3821f3684c03/stdlib/sql.go#L58-L65

// # PostgreSQL Specific Data Types
//
// The pgtype package provides support for PostgreSQL specific types. *pgtype.Map.SQLScanner is an adapter that makes
// these types usable as a sql.Scanner.
//
// m := pgtype.NewMap()
// var a []int64
// err := db.QueryRow("select '{1,2,3}'::bigint[]").Scan(m.SQLScanner(&a))