英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论