126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
|
package pgtype
|
||
|
|
||
|
import (
|
||
|
"database/sql/driver"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
// ArrayGetter is a type that can be converted into a PostgreSQL array.
|
||
|
|
||
|
// RecordCodec is a codec for the generic PostgreSQL record type such as is created with the "row" function. Record can
|
||
|
// only decode the binary format. The text format output format from PostgreSQL does not include type information and
|
||
|
// is therefore impossible to decode. Encoding is impossible because PostgreSQL does not support input of generic
|
||
|
// records.
|
||
|
type RecordCodec struct{}
|
||
|
|
||
|
func (RecordCodec) FormatSupported(format int16) bool {
|
||
|
return format == BinaryFormatCode
|
||
|
}
|
||
|
|
||
|
func (RecordCodec) PreferredFormat() int16 {
|
||
|
return BinaryFormatCode
|
||
|
}
|
||
|
|
||
|
func (RecordCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (RecordCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||
|
if format == BinaryFormatCode {
|
||
|
switch target.(type) {
|
||
|
case CompositeIndexScanner:
|
||
|
return &scanPlanBinaryRecordToCompositeIndexScanner{m: m}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type scanPlanBinaryRecordToCompositeIndexScanner struct {
|
||
|
m *Map
|
||
|
}
|
||
|
|
||
|
func (plan *scanPlanBinaryRecordToCompositeIndexScanner) Scan(src []byte, target any) error {
|
||
|
targetScanner := (target).(CompositeIndexScanner)
|
||
|
|
||
|
if src == nil {
|
||
|
return targetScanner.ScanNull()
|
||
|
}
|
||
|
|
||
|
scanner := NewCompositeBinaryScanner(plan.m, src)
|
||
|
for i := 0; scanner.Next(); i++ {
|
||
|
fieldTarget := targetScanner.ScanIndex(i)
|
||
|
if fieldTarget != nil {
|
||
|
fieldPlan := plan.m.PlanScan(scanner.OID(), BinaryFormatCode, fieldTarget)
|
||
|
if fieldPlan == nil {
|
||
|
return fmt.Errorf("unable to scan OID %d in binary format into %v", scanner.OID(), fieldTarget)
|
||
|
}
|
||
|
|
||
|
err := fieldPlan.Scan(scanner.Bytes(), fieldTarget)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := scanner.Err(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (RecordCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||
|
if src == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
switch format {
|
||
|
case TextFormatCode:
|
||
|
return string(src), nil
|
||
|
case BinaryFormatCode:
|
||
|
buf := make([]byte, len(src))
|
||
|
copy(buf, src)
|
||
|
return buf, nil
|
||
|
default:
|
||
|
return nil, fmt.Errorf("unknown format code %d", format)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (RecordCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||
|
if src == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
switch format {
|
||
|
case TextFormatCode:
|
||
|
return string(src), nil
|
||
|
case BinaryFormatCode:
|
||
|
scanner := NewCompositeBinaryScanner(m, src)
|
||
|
values := make([]any, scanner.FieldCount())
|
||
|
for i := 0; scanner.Next(); i++ {
|
||
|
var v any
|
||
|
fieldPlan := m.PlanScan(scanner.OID(), BinaryFormatCode, &v)
|
||
|
if fieldPlan == nil {
|
||
|
return nil, fmt.Errorf("unable to scan OID %d in binary format into %v", scanner.OID(), v)
|
||
|
}
|
||
|
|
||
|
err := fieldPlan.Scan(scanner.Bytes(), &v)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
values[i] = v
|
||
|
}
|
||
|
|
||
|
if err := scanner.Err(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return values, nil
|
||
|
default:
|
||
|
return nil, fmt.Errorf("unknown format code %d", format)
|
||
|
}
|
||
|
|
||
|
}
|