需要使用jack/pgx更新golang中复合类型的PSQL行

kr98yfug  于 2023-03-16  发布在  Go
关注(0)|答案(1)|浏览(210)

我正在尝试使用jackc/pgx在PostgreSQL中插入/更新数据到一个包含复合类型列的表中。这是一个golan结构体编写的表类型:

// Added this struct as a Types in PSQL
type DayPriceModel struct {
    Date                time.Time `json:"date"`
    High                float32   `json:"high"`
    Low                 float32   `json:"low"`
    Open                float32   `json:"open"`
    Close               float32   `json:"close"`
}

//表中的2列

type SecuritiesPriceHistoryModel struct {
    Symbol  string          `json:"symbol"`
    History []DayPriceModel `json:"history"`
}

history是一个复合类型的数组,我在PSQL中将其定义为DayPriceModel。我想使用jack/pgx在golang中向history追加一个新元素
到目前为止,我已经编写了给定的代码:

// The code for newType was generated by ChatGPT so it might or might not be correct. Feel free to overwrite this part.
newType, _ := pgtype.NewCompositeType("day_price_model", []pgtype.CompositeTypeField{
            {Name: "date", OID: pgtype.DateOID},
            {Name: "high", OID: pgtype.Float4OID},
            {Name: "low", OID: pgtype.Float4OID},
            {Name: "open", OID: pgtype.Float4OID},
            {Name: "close", OID: pgtype.Float4OID},
        }, (*pgtype.ConnInfo)(pool.Config().ConnConfig.TLSConfig.ClientCAs))

_, err = pool.Exec(context.Background(), `UPDATE equity.securities_price_history
SET history = $1::equity.day_price[] || history WHERE symbol = $2`,
composite_value_here, "something") // unable to form the composite_value_here variable

使用jack/pgx,如何从复合类型创建新的复合值以写入PSQL查询。

iibxawm4

iibxawm41#

要求

  • 复合类型使用psql定义
  • 更新/插入复合类型的类型数组的列
  • 使用github.com/jackc/pgx/v5

最简单的方法是使用pgx.LoadType()/pgx.RegisterType()和已经在db中定义的复合类型。
因为我们有一个复合类型的数组,所以我们需要对复合类型本身和数组类型都做这件事(你可以分别用select 'day_price_model'::regtype::oid;select 'day_price_model[]'::regtype::oid;来检查oid是不同的)。
对于配准,我们可以从v5/pgtype documentation中获取RegisterDataTypes
假设类型是使用psql创建的,如下所示:

create type day_price_model as (
    date date,
    high float,
    low  float,               
    open float,
    close float
);

RegisterDataTypes可能类似于:

func RegisterDataTypes(ctx context.Context, conn *pgx.Conn) error {
    dataTypeNames := []string{
        "day_price_model",
        "day_price_model[]",
    }

    for _, typeName := range dataTypeNames {
        dataType, err := conn.LoadType(ctx, typeName)
        if err != nil {
            return err
        }
        conn.TypeMap().RegisterType(dataType)
    }

    return nil
}

注意上面两种类型的数据类型名称。
给予一些模拟数据:

history := []DayPriceModel{
        {time.Now().AddDate(0, 0, -2), 4, 1, 2, 3},
        {time.Now().AddDate(0, 0, -1), 10, 5, 6, 7},
    }

insert将简单地表示为:

insertStmt := `INSERT INTO securities_price_history VALUES ($1, $2)`
_, err = conn.Exec(context.Background(), insertStmt, "something", history)

update是这样的:

updateStmt := `UPDATE securities_price_history SET history = $1 WHERE symbol = $2`
_, err = conn.Exec(context.Background(), updateStmt, newHistory, "something")

测试

为了有一个完整的、自包含的测试示例,我们需要一个数据库和一个小的测试程序,考虑到上面提到的几点。

数据库

使用psql的测试数据库可以像这样创建:

create database equity;

\c equity

create type day_price_model as (
    date date,
    high float,
    low  float,               
    open float,
    close float
);

create table securities_price_history (
    symbol varchar,
    history day_price_model[]
);

围棋项目

package main

import (
    "context"
    "fmt"
    "github.com/jackc/pgx/v5"
    _ "github.com/jackc/pgx/v5"
    _ "github.com/jackc/pgx/v5/stdlib"
    "log"
    "time"
)

type DayPriceModel struct {
    Date  time.Time `json:"date"`
    High  float32   `json:"high"`
    Low   float32   `json:"low"`
    Open  float32   `json:"open"`
    Close float32   `json:"close"`
}

type SecuritiesPriceHistoryModel struct {
    Symbol  string          `json:"symbol"`
    History []DayPriceModel `json:"history"`
}

func RegisterDataTypes(ctx context.Context, conn *pgx.Conn) error {
    dataTypeNames := []string{
        "day_price_model",
        "day_price_model[]",
    }

    for _, typeName := range dataTypeNames {
        dataType, err := conn.LoadType(ctx, typeName)
        if err != nil {
            return err
        }
        conn.TypeMap().RegisterType(dataType)
    }

    return nil
}

func main() {
    dsn := "host=localhost port=5432 user=postgres password=postgres dbname=equity"
    conn, err := pgx.Connect(context.Background(), dsn)
    if err != nil {
        log.Fatal(err)
    }

    defer conn.Close(context.Background())

    err = RegisterDataTypes(context.Background(), conn)
    if err != nil {
        log.Fatal(err)
    }

    history := []DayPriceModel{
        {time.Now().AddDate(0, 0, -2), 4, 1, 2, 3},
        {time.Now().AddDate(0, 0, -1), 10, 5, 6, 7},
    }

    insertStmt := `INSERT INTO securities_price_history VALUES ($1, $2)`
    _, err = conn.Exec(context.Background(), insertStmt, "something", history)
    if err != nil {
        log.Fatal(err)
    }

    sphm := &SecuritiesPriceHistoryModel{}
    selectStmt := `SELECT (symbol, history) FROM securities_price_history WHERE symbol=$1`
    err = conn.QueryRow(context.Background(), selectStmt, "something").Scan(sphm)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("after insert: %v\n", sphm)

    newHistory := append(history, DayPriceModel{time.Now(), 6, 3, 4, 5})

    updateStmt := `UPDATE securities_price_history SET history = $1 WHERE symbol = $2`
    _, err = conn.Exec(context.Background(), updateStmt, newHistory, "something")
    if err != nil {
        log.Fatal(err)
    }

    err = conn.QueryRow(context.Background(), selectStmt, "something").Scan(sphm)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("after update: %v\n", sphm)
}

测试程序的输出为:

after insert: &{something [{2023-03-10 00:00:00 +0000 UTC 4 1 2 3} {2023-03-11 00:00:00 +0000 UTC 10 5 6 7}]}
after update: &{something [{2023-03-10 00:00:00 +0000 UTC 4 1 2 3} {2023-03-11 00:00:00 +0000 UTC 10 5 6 7} {2023-03-12 00:00:00 +0000 UTC 6 3 4 5}]}

正如您所看到的,已经插入了一条记录,更新已经成功执行,因为数组中有三个元素,并且可以从DB中读取数据。
如果你想再次尝试,你显然应该再次从表中删除数据,例如用psql,但是,这个例子应该尽可能小,以便用本地数据库运行一个例子。

相关问题