sqlc + pgx:用户定义枚举数组:无法扫描文本格式的未知类型(OID 16385)

use*_*072 6 arrays enums go pgx sqlc

我正在使用sqlcand pgx/v5,并且对于用户定义的枚举类型的 postgres 数组出现以下错误:

Error: can't scan into dest[1]: cannot scan unknown type (OID 16385) in text format into *pgtype.Array[my-app/sqlc.Option]

架构和查询:

CREATE TYPE option AS ENUM (
    'OPT_1',
    'OPT_2',
    'OPT_3'
);

CREATE TABLE IF NOT EXISTS blah (
    id BIGINT PRIMARY KEY,
    options option[] NOT NULL DEFAULT '{OPT_1}'
);

-- name: CreateBlah :one
INSERT INTO blah (
    id
) VALUES (
    $1
)
RETURNING *;
Run Code Online (Sandbox Code Playgroud)

sqlc似乎正确生成了类型:

CREATE TYPE option AS ENUM (
    'OPT_1',
    'OPT_2',
    'OPT_3'
);

CREATE TABLE IF NOT EXISTS blah (
    id BIGINT PRIMARY KEY,
    options option[] NOT NULL DEFAULT '{OPT_1}'
);

-- name: CreateBlah :one
INSERT INTO blah (
    id
) VALUES (
    $1
)
RETURNING *;
Run Code Online (Sandbox Code Playgroud)

我可以通过定义自己的类型并实现接口,scanner然后在配置中指定覆盖来解决这个问题sqlc

// Code generated by sqlc. DO NOT EDIT.
// versions:
//   sqlc v1.16.0

package sqlc

import (
    "database/sql/driver"
    "fmt"

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

type Option string

const (
    OptionOPT1 Option = "OPT_1"
    OptionOPT2 Option = "OPT_2"
    OptionOPT3 Option = "OPT_3"
)

func (e *Option) Scan(src interface{}) error {
    switch s := src.(type) {
    case []byte:
        *e = Option(s)
    case string:
        *e = Option(s)
    default:
        return fmt.Errorf("unsupported scan type for Option: %T", src)
    }
    return nil
}

type NullOption struct {
    Option Option
    Valid  bool // Valid is true if Option is not NULL
}

// Scan implements the Scanner interface.
func (ns *NullOption) Scan(value interface{}) error {
    if value == nil {
        ns.Option, ns.Valid = "", false
        return nil
    }
    ns.Valid = true
    return ns.Option.Scan(value)
}

// Value implements the driver Valuer interface.
func (ns NullOption) Value() (driver.Value, error) {
    if !ns.Valid {
        return nil, nil
    }
    return string(ns.Option), nil
}

func (e Option) Valid() bool {
    switch e {
    case OptionOPT1,
        OptionOPT2,
        OptionOPT3:
        return true
    }
    return false
}

type Blah struct {
    ID      int64
    Options pgtype.Array[Option]
}
Run Code Online (Sandbox Code Playgroud)
// sqlc.yaml
...
  overrides:
    - column: "blah.options"
      go_type: "myapp/pgx/types.Options" // <-- cannot be "sqlc.Options"
Run Code Online (Sandbox Code Playgroud)

但底层类型必须是pgtype.Array[string],但不能是pgtype.Array[Option],因为:

  1. sqlc无法覆盖与生成代码相同的包中的类型
  2. 我无法在定义的类型中导入sqlc生成的类型,因为它会导致导入周期(pkg importing和 pkg importing )OptionOptionstypessqlc.Optionsqlctypes.Options

Option这意味着我失去了类型安全性以及sqlc.

从这个pgx/v5 github 问题来看,我认为我需要使用该pgx/v5 SQLScanner类型并调用它的RegisterDefaultPgType方法,但是,我不确定这是否准确,或者如何实际做到这一点。

pgx在不失去类型安全性的情况下识别用户定义的枚举类型的 postgres 数组的正确方法是什么?

Ger*_*dal 1

在 pgx 上注册类型对我有用

    dataTypeNames := []string{
        "my_custom_type_name",
        // I think this one prefixed with "_" is used for arrays
        "_my_custom_type_name",
    }
    conn := // get *pgx.Conn
    for _, typeName := range dataTypeNames {
        dataType, _ := conn.LoadType(ctx, typeName)
        conn.TypeMap().RegisterType(dataType)
    }
Run Code Online (Sandbox Code Playgroud)

有了这样的东西,你就可以做到这一点,而无需手动定义自定义类型。