package pgtype
import (
)
type CompositeIndexGetter interface {
IsNull() bool
Index(i int) any
}
type CompositeIndexScanner interface {
ScanNull() error
ScanIndex(i int) any
}
type CompositeCodecField struct {
Name string
Type *Type
}
type CompositeCodec struct {
Fields []CompositeCodecField
}
func ( *CompositeCodec) ( int16) bool {
for , := range .Fields {
if !.Type.Codec.FormatSupported() {
return false
}
}
return true
}
func ( *CompositeCodec) () int16 {
if .FormatSupported(BinaryFormatCode) {
return BinaryFormatCode
}
return TextFormatCode
}
func ( *CompositeCodec) ( *Map, uint32, int16, any) EncodePlan {
if , := .(CompositeIndexGetter); ! {
return nil
}
switch {
case BinaryFormatCode:
return &encodePlanCompositeCodecCompositeIndexGetterToBinary{cc: , m: }
case TextFormatCode:
return &encodePlanCompositeCodecCompositeIndexGetterToText{cc: , m: }
}
return nil
}
type encodePlanCompositeCodecCompositeIndexGetterToBinary struct {
cc *CompositeCodec
m *Map
}
func ( *encodePlanCompositeCodecCompositeIndexGetterToBinary) ( any, []byte) ( []byte, error) {
:= .(CompositeIndexGetter)
if .IsNull() {
return nil, nil
}
:= NewCompositeBinaryBuilder(.m, )
for , := range .cc.Fields {
.AppendValue(.Type.OID, .Index())
}
return .Finish()
}
type encodePlanCompositeCodecCompositeIndexGetterToText struct {
cc *CompositeCodec
m *Map
}
func ( *encodePlanCompositeCodecCompositeIndexGetterToText) ( any, []byte) ( []byte, error) {
:= .(CompositeIndexGetter)
if .IsNull() {
return nil, nil
}
:= NewCompositeTextBuilder(.m, )
for , := range .cc.Fields {
.AppendValue(.Type.OID, .Index())
}
return .Finish()
}
func ( *CompositeCodec) ( *Map, uint32, int16, any) ScanPlan {
switch {
case BinaryFormatCode:
switch .(type) {
case CompositeIndexScanner:
return &scanPlanBinaryCompositeToCompositeIndexScanner{cc: , m: }
}
case TextFormatCode:
switch .(type) {
case CompositeIndexScanner:
return &scanPlanTextCompositeToCompositeIndexScanner{cc: , m: }
}
}
return nil
}
type scanPlanBinaryCompositeToCompositeIndexScanner struct {
cc *CompositeCodec
m *Map
}
func ( *scanPlanBinaryCompositeToCompositeIndexScanner) ( []byte, any) error {
:= ().(CompositeIndexScanner)
if == nil {
return .ScanNull()
}
:= NewCompositeBinaryScanner(.m, )
for , := range .cc.Fields {
if .Next() {
:= .ScanIndex()
if != nil {
:= .m.PlanScan(.Type.OID, BinaryFormatCode, )
if == nil {
return fmt.Errorf("unable to encode %v into OID %d in binary format", , .Type.OID)
}
:= .Scan(.Bytes(), )
if != nil {
return
}
}
} else {
return errors.New("read past end of composite")
}
}
if := .Err(); != nil {
return
}
return nil
}
type scanPlanTextCompositeToCompositeIndexScanner struct {
cc *CompositeCodec
m *Map
}
func ( *scanPlanTextCompositeToCompositeIndexScanner) ( []byte, any) error {
:= ().(CompositeIndexScanner)
if == nil {
return .ScanNull()
}
:= NewCompositeTextScanner(.m, )
for , := range .cc.Fields {
if .Next() {
:= .ScanIndex()
if != nil {
:= .m.PlanScan(.Type.OID, TextFormatCode, )
if == nil {
return fmt.Errorf("unable to encode %v into OID %d in text format", , .Type.OID)
}
:= .Scan(.Bytes(), )
if != nil {
return
}
}
} else {
return errors.New("read past end of composite")
}
}
if := .Err(); != nil {
return
}
return nil
}
func ( *CompositeCodec) ( *Map, uint32, int16, []byte) (driver.Value, error) {
if == nil {
return nil, nil
}
switch {
case TextFormatCode:
return string(), nil
case BinaryFormatCode:
:= make([]byte, len())
copy(, )
return , nil
default:
return nil, fmt.Errorf("unknown format code %d", )
}
}
func ( *CompositeCodec) ( *Map, uint32, int16, []byte) (any, error) {
if == nil {
return nil, nil
}
switch {
case TextFormatCode:
:= NewCompositeTextScanner(, )
:= make(map[string]any, len(.Fields))
for := 0; .Next() && < len(.Fields); ++ {
var any
:= .PlanScan(.Fields[].Type.OID, TextFormatCode, &)
if == nil {
return nil, fmt.Errorf("unable to scan OID %d in text format into %v", .Fields[].Type.OID, )
}
:= .Scan(.Bytes(), &)
if != nil {
return nil,
}
[.Fields[].Name] =
}
if := .Err(); != nil {
return nil,
}
return , nil
case BinaryFormatCode:
:= NewCompositeBinaryScanner(, )
:= make(map[string]any, len(.Fields))
for := 0; .Next() && < len(.Fields); ++ {
var any
:= .PlanScan(.OID(), BinaryFormatCode, &)
if == nil {
return nil, fmt.Errorf("unable to scan OID %d in binary format into %v", .OID(), )
}
:= .Scan(.Bytes(), &)
if != nil {
return nil,
}
[.Fields[].Name] =
}
if := .Err(); != nil {
return nil,
}
return , nil
default:
return nil, fmt.Errorf("unknown format code %d", )
}
}
type CompositeBinaryScanner struct {
m *Map
rp int
src []byte
fieldCount int32
fieldBytes []byte
fieldOID uint32
err error
}
func ( *Map, []byte) *CompositeBinaryScanner {
:= 0
if len([:]) < 4 {
return &CompositeBinaryScanner{err: fmt.Errorf("Record incomplete %v", )}
}
:= int32(binary.BigEndian.Uint32([:]))
+= 4
return &CompositeBinaryScanner{
m: ,
rp: ,
src: ,
fieldCount: ,
}
}
func ( *CompositeBinaryScanner) () bool {
if .err != nil {
return false
}
if .rp == len(.src) {
return false
}
if len(.src[.rp:]) < 8 {
.err = fmt.Errorf("Record incomplete %v", .src)
return false
}
.fieldOID = binary.BigEndian.Uint32(.src[.rp:])
.rp += 4
:= int(int32(binary.BigEndian.Uint32(.src[.rp:])))
.rp += 4
if >= 0 {
if len(.src[.rp:]) < {
.err = fmt.Errorf("Record incomplete rp=%d src=%v", .rp, .src)
return false
}
.fieldBytes = .src[.rp : .rp+]
.rp +=
} else {
.fieldBytes = nil
}
return true
}
func ( *CompositeBinaryScanner) () int {
return int(.fieldCount)
}
func ( *CompositeBinaryScanner) () []byte {
return .fieldBytes
}
func ( *CompositeBinaryScanner) () uint32 {
return .fieldOID
}
func ( *CompositeBinaryScanner) () error {
return .err
}
type CompositeTextScanner struct {
m *Map
rp int
src []byte
fieldBytes []byte
err error
}
func ( *Map, []byte) *CompositeTextScanner {
if len() < 2 {
return &CompositeTextScanner{err: fmt.Errorf("Record incomplete %v", )}
}
if [0] != '(' {
return &CompositeTextScanner{err: fmt.Errorf("composite text format must start with '('")}
}
if [len()-1] != ')' {
return &CompositeTextScanner{err: fmt.Errorf("composite text format must end with ')'")}
}
return &CompositeTextScanner{
m: ,
rp: 1,
src: ,
}
}
func ( *CompositeTextScanner) () bool {
if .err != nil {
return false
}
if .rp == len(.src) {
return false
}
switch .src[.rp] {
case ',', ')':
.rp++
.fieldBytes = nil
return true
case '"':
.rp++
.fieldBytes = make([]byte, 0, 16)
for {
:= .src[.rp]
if == '"' {
.rp++
if .src[.rp] == '"' {
.fieldBytes = append(.fieldBytes, '"')
.rp++
} else {
break
}
} else if == '\\' {
.rp++
.fieldBytes = append(.fieldBytes, .src[.rp])
.rp++
} else {
.fieldBytes = append(.fieldBytes, )
.rp++
}
}
.rp++
return true
default:
:= .rp
for {
:= .src[.rp]
if == ',' || == ')' {
break
}
.rp++
}
.fieldBytes = .src[:.rp]
.rp++
return true
}
}
func ( *CompositeTextScanner) () []byte {
return .fieldBytes
}
func ( *CompositeTextScanner) () error {
return .err
}
type CompositeBinaryBuilder struct {
m *Map
buf []byte
startIdx int
fieldCount uint32
err error
}
func ( *Map, []byte) *CompositeBinaryBuilder {
:= len()
= append(, 0, 0, 0, 0)
return &CompositeBinaryBuilder{m: , buf: , startIdx: }
}
func ( *CompositeBinaryBuilder) ( uint32, any) {
if .err != nil {
return
}
if , := isNilDriverValuer(); {
.buf = pgio.AppendUint32(.buf, )
.buf = pgio.AppendInt32(.buf, -1)
.fieldCount++
return
}
:= .m.PlanEncode(, BinaryFormatCode, )
if == nil {
.err = fmt.Errorf("unable to encode %v into OID %d in binary format", , )
return
}
.buf = pgio.AppendUint32(.buf, )
:= len(.buf)
.buf = pgio.AppendInt32(.buf, -1)
, := .Encode(, .buf)
if != nil {
.err =
return
}
if != nil {
binary.BigEndian.PutUint32([:], uint32(len()-len(.buf)))
.buf =
}
.fieldCount++
}
func ( *CompositeBinaryBuilder) () ([]byte, error) {
if .err != nil {
return nil, .err
}
binary.BigEndian.PutUint32(.buf[.startIdx:], .fieldCount)
return .buf, nil
}
type CompositeTextBuilder struct {
m *Map
buf []byte
startIdx int
fieldCount uint32
err error
fieldBuf [32]byte
}
func ( *Map, []byte) *CompositeTextBuilder {
= append(, '(')
return &CompositeTextBuilder{m: , buf: }
}
func ( *CompositeTextBuilder) ( uint32, any) {
if .err != nil {
return
}
if , := isNilDriverValuer(); {
.buf = append(.buf, ',')
return
}
:= .m.PlanEncode(, TextFormatCode, )
if == nil {
.err = fmt.Errorf("unable to encode %v into OID %d in text format", , )
return
}
, := .Encode(, .fieldBuf[0:0])
if != nil {
.err =
return
}
if != nil {
.buf = append(.buf, quoteCompositeFieldIfNeeded(string())...)
}
.buf = append(.buf, ',')
}
func ( *CompositeTextBuilder) () ([]byte, error) {
if .err != nil {
return nil, .err
}
.buf[len(.buf)-1] = ')'
return .buf, nil
}
var quoteCompositeReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)
func ( string) string {
return `"` + quoteCompositeReplacer.Replace() + `"`
}
func ( string) string {
if == "" || [0] == ' ' || [len()-1] == ' ' || strings.ContainsAny(, `(),"\`) {
return quoteCompositeField()
}
return
}
type CompositeFields []any
func ( CompositeFields) () {}
func ( CompositeFields) () bool {
return == nil
}
func ( CompositeFields) ( int) any {
return []
}
func ( CompositeFields) () error {
return fmt.Errorf("cannot scan NULL into CompositeFields")
}
func ( CompositeFields) ( int) any {
return []
}