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

n6lpvg4x  于 2023-05-11  发布在  Go
关注(0)|答案(1)|浏览(135)

我正在使用sqlcpgx/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]
schema和query:

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 *;

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]
}

我可以通过定义自己的类型并实现scanner接口,然后在sqlc配置中指定重写来解决它:

package types

import (
    "fmt"
    "strings"

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

type Options pgtype.Array[string] // <-- cannot be pgtype.Array[sqlc.Option], causes import cycle

func (opts *Options) Scan(src any) error {
    opts, ok := src.(string)
    if !ok {
        return fmt.Errorf("unsupported scan type for Options: %T", src)
    }

    options := strings.Split(strings.Trim(opts, "{}"), ",")
    *opts = Options(pgtype.Array[string]{Elements: options, Valid: true})

    return nil
}
// sqlc.yaml
...
  overrides:
    - column: "blah.options"
      go_type: "myapp/pgx/types.Options" // <-- cannot be "sqlc.Options"

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

  1. sqlc不能重写与生成的代码相同的包中的类型
    1.我无法在定义的Options类型中导入sqlc生成的Option类型,因为这会导致导入循环(pkg types导入sqlc.Option,pkg sqlc导入types.Options
    这意味着我失去了类型安全和sqlc生成的Option类型的其他方法。
    从这个pgx/v5github issue开始,我想我需要使用pgx/v5SQLScanner类型并调用它的RegisterDefaultPgType方法,但是,我不确定这是否准确,或者如何实际做到这一点。
    pgx识别用户定义的枚举类型的postgres数组而不失去类型安全性的正确方法是什么?
jmo0nnb3

jmo0nnb31#

在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)
    }

使用something like this,您可以在不手动定义自定义类型的情况下完成此操作。

相关问题