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

huangapple go评论94阅读模式
英文:

Need to update PSQL row of a composite type in golang with jack/pgx

问题

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

// 将此结构体作为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"`
}

// 我的表中的两列

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

history是一个复合类型的数组,在PSQL中我已将其定义为DayPriceModel。我想使用jack/pgx在golang中将一个新元素追加到history中。

到目前为止,我已经编写了以下代码:

// newType的代码是由ChatGPT生成的,所以它可能正确也可能不正确。请随意覆盖这部分。
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") // 无法形成composite_value_here变量

使用jack/pgx,我该如何从复合类型创建一个新的复合值,以便在PSQL查询中写入。

英文:

I am trying to insert/update data in PostgreSQL using jackc/pgx into a table that has column of composite type. This is the table type written as a golan struct:

// 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"`
}

// The 2 columns in my table

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

The column history is an array of composite type which I have defined as DayPriceModel in PSQL. I want to append a new element to history in golang using jack/pgx

I have so far written the given code:

// 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

Using jack/pgx, how do I create a new composite value from a composite type to write in the PSQL query.

答案1

得分: 1

要求

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

最简单的方法是使用pgx.LoadType()/pgx.RegisterType(),并使用数据库中已定义的复合类型。

由于我们有一个复合类型数组,我们需要对复合类型本身和数组类型都进行注册(可以使用select 'day_price_model'::regtype::oid;select 'day_price_model[]'::regtype::oid;检查oid是否不同)。

我们可以使用v5/pgtype文档中的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},
}

插入操作将简单地如下所示:

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

更新操作可能如下所示:

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[]
);

Go程序

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

如您所见,已插入一条记录,更新已成功执行,因为数组中有三个元素,并且可以从数据库中读取数据。

如果您想重复尝试,显然应该从表中删除数据,例如使用psql。然而,这只是一个尽可能小的示例,以运行一个带有本地数据库的示例。

英文:

Requirements

  • composite type is defined using psql
  • updating/inserting into a column of type array of composite type
  • using github.com/jackc/pgx/v5

The easiest way is to use pgx.LoadType()/pgx.RegisterType() and use the composite type already defined in the db.

Since we have an array of composite type we need to do it for both, the composite type itself and the array type (you can check with select 'day_price_model'::regtype::oid; respectively select 'day_price_model[]'::regtype::oid; that the oids are different).

For registration we can take RegisterDataTypes from the v5/pgtype documentation.

Assuming the type was created with psql as follows:

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

the RegisterDataTypes could look sth like:

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
}

Note the above data type names for both types.

Give some mock data:

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

an insert would be simply:

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

and the update something like this:

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

Test

In order to have a complete, self-contained example of a test, we need a db and a small test program, taking into account the points mentioned above.

DB

A test DB using psql could be created like this:

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[]
);

Go Program

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

The output of the test program is:

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

As you can see, a record has been inserted, the update has been performed successfully, because there are then three elements in the array and the data can be read from the DB.

If you then want to repeat the attempt, you should obviously remove the data from the table again, e.g. with psql. However, this should only be as small an example as possible to run an example with a local db.

huangapple
  • 本文由 发表于 2023年3月7日 13:49:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75658429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定