mirror of https://github.com/jackc/pgx.git
130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
package pgtype
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"fmt"
|
|
)
|
|
|
|
type JSONBCodec struct {
|
|
Marshal func(v any) ([]byte, error)
|
|
Unmarshal func(data []byte, v any) error
|
|
}
|
|
|
|
func (*JSONBCodec) FormatSupported(format int16) bool {
|
|
return format == TextFormatCode || format == BinaryFormatCode
|
|
}
|
|
|
|
func (*JSONBCodec) PreferredFormat() int16 {
|
|
return TextFormatCode
|
|
}
|
|
|
|
func (c *JSONBCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
plan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, TextFormatCode, value)
|
|
if plan != nil {
|
|
return &encodePlanJSONBCodecBinaryWrapper{textPlan: plan}
|
|
}
|
|
case TextFormatCode:
|
|
return (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, format, value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type encodePlanJSONBCodecBinaryWrapper struct {
|
|
textPlan EncodePlan
|
|
}
|
|
|
|
func (plan *encodePlanJSONBCodecBinaryWrapper) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
buf = append(buf, 1)
|
|
return plan.textPlan.Encode(value, buf)
|
|
}
|
|
|
|
func (c *JSONBCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
plan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, TextFormatCode, target)
|
|
if plan != nil {
|
|
return &scanPlanJSONBCodecBinaryUnwrapper{textPlan: plan}
|
|
}
|
|
case TextFormatCode:
|
|
return (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, format, target)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type scanPlanJSONBCodecBinaryUnwrapper struct {
|
|
textPlan ScanPlan
|
|
}
|
|
|
|
func (plan *scanPlanJSONBCodecBinaryUnwrapper) Scan(src []byte, dst any) error {
|
|
if src == nil {
|
|
return plan.textPlan.Scan(src, dst)
|
|
}
|
|
|
|
if len(src) == 0 {
|
|
return fmt.Errorf("jsonb too short")
|
|
}
|
|
|
|
if src[0] != 1 {
|
|
return fmt.Errorf("unknown jsonb version number %d", src[0])
|
|
}
|
|
|
|
return plan.textPlan.Scan(src[1:], dst)
|
|
}
|
|
|
|
func (c *JSONBCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
|
if src == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
if len(src) == 0 {
|
|
return nil, fmt.Errorf("jsonb too short")
|
|
}
|
|
|
|
if src[0] != 1 {
|
|
return nil, fmt.Errorf("unknown jsonb version number %d", src[0])
|
|
}
|
|
|
|
dstBuf := make([]byte, len(src)-1)
|
|
copy(dstBuf, src[1:])
|
|
return dstBuf, nil
|
|
case TextFormatCode:
|
|
dstBuf := make([]byte, len(src))
|
|
copy(dstBuf, src)
|
|
return dstBuf, nil
|
|
default:
|
|
return nil, fmt.Errorf("unknown format code: %v", format)
|
|
}
|
|
}
|
|
|
|
func (c *JSONBCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
|
if src == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
if len(src) == 0 {
|
|
return nil, fmt.Errorf("jsonb too short")
|
|
}
|
|
|
|
if src[0] != 1 {
|
|
return nil, fmt.Errorf("unknown jsonb version number %d", src[0])
|
|
}
|
|
|
|
src = src[1:]
|
|
case TextFormatCode:
|
|
default:
|
|
return nil, fmt.Errorf("unknown format code: %v", format)
|
|
}
|
|
|
|
var dst any
|
|
err := c.Unmarshal(src, &dst)
|
|
return dst, err
|
|
}
|