gotosocial/vendor/github.com/jackc/pgtype/tid.go
tobi 2dc9fc1626
Pg to bun (#148)
* start moving to bun

* changing more stuff

* more

* and yet more

* tests passing

* seems stable now

* more big changes

* small fix

* little fixes
2021-08-25 15:34:33 +02:00

157 lines
3.2 KiB
Go

package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"strconv"
"strings"
"github.com/jackc/pgio"
)
// TID is PostgreSQL's Tuple Identifier type.
//
// When one does
//
// select ctid, * from some_table;
//
// it is the data type of the ctid hidden system column.
//
// It is currently implemented as a pair unsigned two byte integers.
// Its conversion functions can be found in src/backend/utils/adt/tid.c
// in the PostgreSQL sources.
type TID struct {
BlockNumber uint32
OffsetNumber uint16
Status Status
}
func (dst *TID) Set(src interface{}) error {
return fmt.Errorf("cannot convert %v to TID", src)
}
func (dst TID) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *TID) AssignTo(dst interface{}) error {
if src.Status == Present {
switch v := dst.(type) {
case *string:
*v = fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber)
return nil
default:
if nextDst, retry := GetAssignToDstType(dst); retry {
return src.AssignTo(nextDst)
}
return fmt.Errorf("unable to assign to %T", dst)
}
}
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *TID) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = TID{Status: Null}
return nil
}
if len(src) < 5 {
return fmt.Errorf("invalid length for tid: %v", len(src))
}
parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
if len(parts) < 2 {
return fmt.Errorf("invalid format for tid")
}
blockNumber, err := strconv.ParseUint(parts[0], 10, 32)
if err != nil {
return err
}
offsetNumber, err := strconv.ParseUint(parts[1], 10, 16)
if err != nil {
return err
}
*dst = TID{BlockNumber: uint32(blockNumber), OffsetNumber: uint16(offsetNumber), Status: Present}
return nil
}
func (dst *TID) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = TID{Status: Null}
return nil
}
if len(src) != 6 {
return fmt.Errorf("invalid length for tid: %v", len(src))
}
*dst = TID{
BlockNumber: binary.BigEndian.Uint32(src),
OffsetNumber: binary.BigEndian.Uint16(src[4:]),
Status: Present,
}
return nil
}
func (src TID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = append(buf, fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber)...)
return buf, nil
}
func (src TID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = pgio.AppendUint32(buf, src.BlockNumber)
buf = pgio.AppendUint16(buf, src.OffsetNumber)
return buf, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *TID) Scan(src interface{}) error {
if src == nil {
*dst = TID{Status: Null}
return nil
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
return dst.DecodeText(nil, srcCopy)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src TID) Value() (driver.Value, error) {
return EncodeValueText(src)
}