如何使用jackc/pgx在Go中更新PostgreSQL复合类型数组字段
使用jackc/pgx向PostgreSQL复合类型数组追加元素
核心问题修正
你之前的代码存在两个关键错误:
- 错误地将TLS配置的
ClientCAs强制转换为pgtype.ConnInfo,ConnInfo必须从实际数据库连接中获取,它包含PostgreSQL的类型元数据。 - 无需构造复合类型数组来执行
||拼接,使用PostgreSQL内置的array_append函数更简洁高效。
解决方案步骤
1. 注册复合类型到ConnInfo
首先需要在数据库连接的ConnInfo中注册自定义复合类型,确保pgx能正确识别和转换类型。建议在连接池初始化完成后执行此操作:
// 获取一个连接用于获取ConnInfo conn, err := pool.Acquire(context.Background()) if err != nil { // 实际代码中替换为业务错误处理逻辑 panic(err) } defer conn.Release() ci := conn.ConnInfo() // 注册equity.day_price复合类型 dayPriceType, err := pgtype.NewCompositeType( "equity.day_price", []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}, }, ci, ) if err != nil { panic(err) } ci.RegisterCompositeType(dayPriceType)
2. 构造复合类型值并执行更新
有两种方式构造复合类型值:
方式一:手动构造CompositeValue
直接创建pgtype.CompositeValue实例,适用于临时场景:
// 定义要追加的新数据 newDayPrice := DayPriceModel{ Date: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), High: 150.5, Low: 145.2, Open: 147.0, Close: 149.8, } // 获取已注册的复合类型 ct, ok := ci.CompositeType("equity.day_price") if !ok { panic("复合类型未注册") } // 构造复合类型值 compositeVal := pgtype.CompositeValue{ Type: ct, Fields: []pgtype.Value{ &pgtype.Date{Time: newDayPrice.Date, Valid: true}, &pgtype.Float4{Float32: newDayPrice.High, Valid: true}, &pgtype.Float4{Float32: newDayPrice.Low, Valid: true}, &pgtype.Float4{Float32: newDayPrice.Open, Valid: true}, &pgtype.Float4{Float32: newDayPrice.Close, Valid: true}, }, Valid: true, } // 执行更新语句 _, err = pool.Exec(context.Background(), ` UPDATE equity.securities_price_history SET history = array_append(history, $1) WHERE symbol = $2 `, compositeVal, "something") if err != nil { panic(err) }
方式二:实现pgx.Valuer接口(推荐)
为DayPriceModel实现pgx.Valuer接口,后续可直接将结构体作为参数传入,无需手动构造复合值:
// 全局保存ConnInfo(初始化时设置) var globalConnInfo *pgtype.ConnInfo func (d DayPriceModel) Value() (driver.Value, error) { ct, ok := globalConnInfo.CompositeType("equity.day_price") if !ok { return nil, fmt.Errorf("未找到复合类型equity.day_price") } fields := []pgtype.Value{ &pgtype.Date{Time: d.Date, Valid: !d.Date.IsZero()}, &pgtype.Float4{Float32: d.High, Valid: true}, &pgtype.Float4{Float32: d.Low, Valid: true}, &pgtype.Float4{Float32: d.Open, Valid: true}, &pgtype.Float4{Float32: d.Close, Valid: true}, } cv := pgtype.CompositeValue{Type: ct, Fields: fields, Valid: true} return pgx.EncodeValue(globalConnInfo, cv) }
之后执行更新的代码会非常简洁:
newDayPrice := DayPriceModel{ Date: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), High: 150.5, Low: 145.2, Open: 147.0, Close: 149.8, } _, err = pool.Exec(context.Background(), ` UPDATE equity.securities_price_history SET history = array_append(history, $1) WHERE symbol = $2 `, newDayPrice, "something") if err != nil { // 错误处理逻辑 }
内容的提问来源于stack exchange,提问作者Krushnal Patel
相关产品推荐
相关产品推荐

