pgtype.Encode(Binary|Text) do not write length

To aid in composability, these methods no longer write their own length. This
is especially useful for text formatted arrays and may be useful for future
database/sql compatibility. It also makes the code a little simpler as the
types no longer have to compute their own size.

Along with this, these methods cannot encode NULL. They now return a boolean
if they are NULL. This also benefits text array encoding as numeric arrays
require NULL to be exactly `NULL` while string arrays require NULL to be
`"NULL"`.
v3-numeric-wip
Jack Christensen 2017-03-11 12:32:33 -06:00
parent 6c26c3a4a3
commit 1f3e484ca1
38 changed files with 1271 additions and 1319 deletions

View File

@ -5,8 +5,6 @@ import (
"io" "io"
"reflect" "reflect"
"strconv" "strconv"
"github.com/jackc/pgx/pgio"
) )
type Bool struct { type Bool struct {
@ -100,14 +98,12 @@ func (dst *Bool) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Bool) EncodeText(w io.Writer) error { func (src Bool) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
} return true, nil
case Undefined:
_, err := pgio.WriteInt32(w, 1) return false, errUndefined
if err != nil {
return nil
} }
var buf []byte var buf []byte
@ -117,18 +113,16 @@ func (src Bool) EncodeText(w io.Writer) error {
buf = []byte{'f'} buf = []byte{'f'}
} }
_, err = w.Write(buf) _, err := w.Write(buf)
return err return false, err
} }
func (src Bool) EncodeBinary(w io.Writer) error { func (src Bool) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
} return true, nil
case Undefined:
_, err := pgio.WriteInt32(w, 1) return false, errUndefined
if err != nil {
return nil
} }
var buf []byte var buf []byte
@ -138,6 +132,6 @@ func (src Bool) EncodeBinary(w io.Writer) error {
buf = []byte{0} buf = []byte{0}
} }
_, err = w.Write(buf) _, err := w.Write(buf)
return err return false, err
} }

View File

@ -152,26 +152,22 @@ func (dst *BoolArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *BoolArray) EncodeText(w io.Writer) error { func (src *BoolArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -185,100 +181,112 @@ func (src *BoolArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *BoolArray) EncodeBinary(w io.Writer) error { func (src *BoolArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, BoolOID) return src.encodeBinary(w, BoolOID)
} }
func (src *BoolArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *BoolArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -5,8 +5,6 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"github.com/jackc/pgx/pgio"
) )
type Bytea struct { type Bytea struct {
@ -101,37 +99,31 @@ func (dst *Bytea) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Bytea) EncodeText(w io.Writer) error { func (src Bytea) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
str := hex.EncodeToString(src.Bytes) _, err := io.WriteString(w, `\x`)
_, err := pgio.WriteInt32(w, int32(len(str)+2))
if err != nil { if err != nil {
return nil return false, err
} }
_, err = io.WriteString(w, `\x`) _, err = io.WriteString(w, hex.EncodeToString(src.Bytes))
if err != nil { return false, err
return nil
}
_, err = io.WriteString(w, str)
return err
} }
func (src Bytea) EncodeBinary(w io.Writer) error { func (src Bytea) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, int32(len(src.Bytes))) _, err := w.Write(src.Bytes)
if err != nil { return false, err
return nil
}
_, err = w.Write(src.Bytes)
return err
} }

View File

@ -38,10 +38,10 @@ func (dst *CID) DecodeBinary(src []byte) error {
return (*pguint32)(dst).DecodeBinary(src) return (*pguint32)(dst).DecodeBinary(src)
} }
func (src CID) EncodeText(w io.Writer) error { func (src CID) EncodeText(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeText(w) return (pguint32)(src).EncodeText(w)
} }
func (src CID) EncodeBinary(w io.Writer) error { func (src CID) EncodeBinary(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeBinary(w) return (pguint32)(src).EncodeBinary(w)
} }

View File

@ -22,10 +22,10 @@ func (dst *CidrArray) DecodeBinary(src []byte) error {
return (*InetArray)(dst).DecodeBinary(src) return (*InetArray)(dst).DecodeBinary(src)
} }
func (src *CidrArray) EncodeText(w io.Writer) error { func (src *CidrArray) EncodeText(w io.Writer) (bool, error) {
return (*InetArray)(src).EncodeText(w) return (*InetArray)(src).EncodeText(w)
} }
func (src *CidrArray) EncodeBinary(w io.Writer) error { func (src *CidrArray) EncodeBinary(w io.Writer) (bool, error) {
return (*InetArray)(src).encodeBinary(w, CidrOID) return (*InetArray)(src).encodeBinary(w, CidrOID)
} }

View File

@ -116,9 +116,12 @@ func (dst *Date) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Date) EncodeText(w io.Writer) error { func (src Date) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var s string var s string
@ -132,23 +135,16 @@ func (src Date) EncodeText(w io.Writer) error {
s = "-infinity" s = "-infinity"
} }
_, err := pgio.WriteInt32(w, int32(len(s))) _, err := io.WriteString(w, s)
if err != nil { return false, err
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Date) EncodeBinary(w io.Writer) error { func (src Date) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
} return true, nil
case Undefined:
_, err := pgio.WriteInt32(w, 4) return false, errUndefined
if err != nil {
return err
} }
var daysSinceDateEpoch int32 var daysSinceDateEpoch int32
@ -165,6 +161,6 @@ func (src Date) EncodeBinary(w io.Writer) error {
daysSinceDateEpoch = negativeInfinityDayOffset daysSinceDateEpoch = negativeInfinityDayOffset
} }
_, err = pgio.WriteInt32(w, daysSinceDateEpoch) _, err := pgio.WriteInt32(w, daysSinceDateEpoch)
return err return false, err
} }

View File

@ -153,26 +153,22 @@ func (dst *DateArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *DateArray) EncodeText(w io.Writer) error { func (src *DateArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -186,100 +182,112 @@ func (src *DateArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *DateArray) EncodeBinary(w io.Writer) error { func (src *DateArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, DateOID) return src.encodeBinary(w, DateOID)
} }
func (src *DateArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *DateArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -124,30 +124,26 @@ func (dst *Float4) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Float4) EncodeText(w io.Writer) error { func (src Float4) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatFloat(float64(src.Float), 'f', -1, 32) _, err := io.WriteString(w, strconv.FormatFloat(float64(src.Float), 'f', -1, 32))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Float4) EncodeBinary(w io.Writer) error { func (src Float4) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 4) _, err := pgio.WriteInt32(w, int32(math.Float32bits(src.Float)))
if err != nil { return false, err
return err
}
_, err = pgio.WriteInt32(w, int32(math.Float32bits(src.Float)))
return err
} }

View File

@ -152,26 +152,22 @@ func (dst *Float4Array) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *Float4Array) EncodeText(w io.Writer) error { func (src *Float4Array) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -185,100 +181,112 @@ func (src *Float4Array) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *Float4Array) EncodeBinary(w io.Writer) error { func (src *Float4Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Float4OID) return src.encodeBinary(w, Float4OID)
} }
func (src *Float4Array) encodeBinary(w io.Writer, elementOID int32) error { func (src *Float4Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -114,30 +114,26 @@ func (dst *Float8) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Float8) EncodeText(w io.Writer) error { func (src Float8) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatFloat(float64(src.Float), 'f', -1, 64) _, err := io.WriteString(w, strconv.FormatFloat(float64(src.Float), 'f', -1, 64))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Float8) EncodeBinary(w io.Writer) error { func (src Float8) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 8) _, err := pgio.WriteInt64(w, int64(math.Float64bits(src.Float)))
if err != nil { return false, err
return err
}
_, err = pgio.WriteInt64(w, int64(math.Float64bits(src.Float)))
return err
} }

View File

@ -152,26 +152,22 @@ func (dst *Float8Array) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *Float8Array) EncodeText(w io.Writer) error { func (src *Float8Array) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -185,100 +181,112 @@ func (src *Float8Array) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *Float8Array) EncodeBinary(w io.Writer) error { func (src *Float8Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Float8OID) return src.encodeBinary(w, Float8OID)
} }
func (src *Float8Array) encodeBinary(w io.Writer, elementOID int32) error { func (src *Float8Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -144,61 +144,55 @@ func (dst *Inet) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Inet) EncodeText(w io.Writer) error { func (src Inet) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := src.IPNet.String() _, err := io.WriteString(w, src.IPNet.String())
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
// EncodeBinary encodes src into w. // EncodeBinary encodes src into w.
func (src Inet) EncodeBinary(w io.Writer) error { func (src Inet) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var size int32
var family byte var family byte
switch len(src.IPNet.IP) { switch len(src.IPNet.IP) {
case net.IPv4len: case net.IPv4len:
size = 8
family = defaultAFInet family = defaultAFInet
case net.IPv6len: case net.IPv6len:
size = 20
family = defaultAFInet6 family = defaultAFInet6
default: default:
return fmt.Errorf("Unexpected IP length: %v", len(src.IPNet.IP)) return false, fmt.Errorf("Unexpected IP length: %v", len(src.IPNet.IP))
}
if _, err := pgio.WriteInt32(w, size); err != nil {
return err
} }
if err := pgio.WriteByte(w, family); err != nil { if err := pgio.WriteByte(w, family); err != nil {
return err return false, err
} }
ones, _ := src.IPNet.Mask.Size() ones, _ := src.IPNet.Mask.Size()
if err := pgio.WriteByte(w, byte(ones)); err != nil { if err := pgio.WriteByte(w, byte(ones)); err != nil {
return err return false, err
} }
// is_cidr is ignored on server // is_cidr is ignored on server
if err := pgio.WriteByte(w, 0); err != nil { if err := pgio.WriteByte(w, 0); err != nil {
return err return false, err
} }
if err := pgio.WriteByte(w, byte(len(src.IPNet.IP))); err != nil { if err := pgio.WriteByte(w, byte(len(src.IPNet.IP))); err != nil {
return err return false, err
} }
_, err := w.Write(src.IPNet.IP) _, err := w.Write(src.IPNet.IP)
return err return false, err
} }

View File

@ -184,26 +184,22 @@ func (dst *InetArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *InetArray) EncodeText(w io.Writer) error { func (src *InetArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -217,100 +213,112 @@ func (src *InetArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *InetArray) EncodeBinary(w io.Writer) error { func (src *InetArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, InetOID) return src.encodeBinary(w, InetOID)
} }
func (src *InetArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *InetArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -119,30 +119,26 @@ func (dst *Int2) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Int2) EncodeText(w io.Writer) error { func (src Int2) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatInt(int64(src.Int), 10) _, err := io.WriteString(w, strconv.FormatInt(int64(src.Int), 10))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Int2) EncodeBinary(w io.Writer) error { func (src Int2) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 2) _, err := pgio.WriteInt16(w, src.Int)
if err != nil { return false, err
return err
}
_, err = pgio.WriteInt16(w, src.Int)
return err
} }

View File

@ -183,26 +183,22 @@ func (dst *Int2Array) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *Int2Array) EncodeText(w io.Writer) error { func (src *Int2Array) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -216,100 +212,112 @@ func (src *Int2Array) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *Int2Array) EncodeBinary(w io.Writer) error { func (src *Int2Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int2OID) return src.encodeBinary(w, Int2OID)
} }
func (src *Int2Array) encodeBinary(w io.Writer, elementOID int32) error { func (src *Int2Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -110,30 +110,26 @@ func (dst *Int4) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Int4) EncodeText(w io.Writer) error { func (src Int4) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatInt(int64(src.Int), 10) _, err := io.WriteString(w, strconv.FormatInt(int64(src.Int), 10))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Int4) EncodeBinary(w io.Writer) error { func (src Int4) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 4) _, err := pgio.WriteInt32(w, src.Int)
if err != nil { return false, err
return err
}
_, err = pgio.WriteInt32(w, src.Int)
return err
} }

View File

@ -183,26 +183,22 @@ func (dst *Int4Array) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *Int4Array) EncodeText(w io.Writer) error { func (src *Int4Array) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -216,100 +212,112 @@ func (src *Int4Array) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *Int4Array) EncodeBinary(w io.Writer) error { func (src *Int4Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int4OID) return src.encodeBinary(w, Int4OID)
} }
func (src *Int4Array) encodeBinary(w io.Writer, elementOID int32) error { func (src *Int4Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -102,30 +102,26 @@ func (dst *Int8) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Int8) EncodeText(w io.Writer) error { func (src Int8) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatInt(src.Int, 10) _, err := io.WriteString(w, strconv.FormatInt(src.Int, 10))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Int8) EncodeBinary(w io.Writer) error { func (src Int8) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 8) _, err := pgio.WriteInt64(w, src.Int)
if err != nil { return false, err
return err
}
_, err = pgio.WriteInt64(w, src.Int)
return err
} }

View File

@ -183,26 +183,22 @@ func (dst *Int8Array) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *Int8Array) EncodeText(w io.Writer) error { func (src *Int8Array) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -216,100 +212,112 @@ func (src *Int8Array) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *Int8Array) EncodeBinary(w io.Writer) error { func (src *Int8Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int8OID) return src.encodeBinary(w, Int8OID)
} }
func (src *Int8Array) encodeBinary(w io.Writer, elementOID int32) error { func (src *Int8Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -35,10 +35,10 @@ func (dst *Name) DecodeBinary(src []byte) error {
return (*Text)(dst).DecodeBinary(src) return (*Text)(dst).DecodeBinary(src)
} }
func (src Name) EncodeText(w io.Writer) error { func (src Name) EncodeText(w io.Writer) (bool, error) {
return (Text)(src).EncodeText(w) return (Text)(src).EncodeText(w)
} }
func (src Name) EncodeBinary(w io.Writer) error { func (src Name) EncodeBinary(w io.Writer) (bool, error) {
return (Text)(src).EncodeBinary(w) return (Text)(src).EncodeBinary(w)
} }

View File

@ -32,10 +32,10 @@ func (dst *OID) DecodeBinary(src []byte) error {
return (*pguint32)(dst).DecodeBinary(src) return (*pguint32)(dst).DecodeBinary(src)
} }
func (src OID) EncodeText(w io.Writer) error { func (src OID) EncodeText(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeText(w) return (pguint32)(src).EncodeText(w)
} }
func (src OID) EncodeBinary(w io.Writer) error { func (src OID) EncodeBinary(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeBinary(w) return (pguint32)(src).EncodeBinary(w)
} }

View File

@ -3,8 +3,6 @@ package pgtype
import ( import (
"errors" "errors"
"io" "io"
"github.com/jackc/pgx/pgio"
) )
// PostgreSQL oids for common types // PostgreSQL oids for common types
@ -81,23 +79,24 @@ type TextDecoder interface {
DecodeText(src []byte) error DecodeText(src []byte) error
} }
// BinaryEncoder is implemented by types that can encode themselves into the
// PostgreSQL binary wire format.
type BinaryEncoder interface { type BinaryEncoder interface {
EncodeBinary(w io.Writer) error // EncodeBinary should encode the binary format of self to w. If self is the
// SQL value NULL then write nothing and return (true, nil). The caller of
// EncodeBinary is responsible for writing the correct NULL value or the
// length of the data written.
EncodeBinary(w io.Writer) (null bool, err error)
} }
// TextEncoder is implemented by types that can encode themselves into the
// PostgreSQL text wire format.
type TextEncoder interface { type TextEncoder interface {
EncodeText(w io.Writer) error // EncodeText should encode the text format of self to w. If self is the SQL
// value NULL then write nothing and return (true, nil). The caller of
// EncodeText is responsible for writing the correct NULL value or the length
// of the data written.
EncodeText(w io.Writer) (null bool, err error)
} }
var errUndefined = errors.New("cannot encode status undefined") var errUndefined = errors.New("cannot encode status undefined")
func encodeNotPresent(w io.Writer, status Status) (done bool, err error) {
switch status {
case Undefined:
return true, errUndefined
case Null:
_, err = pgio.WriteInt32(w, -1)
return true, err
}
return false, nil
}

View File

@ -60,7 +60,7 @@ type forceTextEncoder struct {
e pgtype.TextEncoder e pgtype.TextEncoder
} }
func (f forceTextEncoder) EncodeText(w io.Writer) error { func (f forceTextEncoder) EncodeText(w io.Writer) (bool, error) {
return f.e.EncodeText(w) return f.e.EncodeText(w)
} }
@ -68,7 +68,7 @@ type forceBinaryEncoder struct {
e pgtype.BinaryEncoder e pgtype.BinaryEncoder
} }
func (f forceBinaryEncoder) EncodeBinary(w io.Writer) error { func (f forceBinaryEncoder) EncodeBinary(w io.Writer) (bool, error) {
return f.e.EncodeBinary(w) return f.e.EncodeBinary(w)
} }

View File

@ -82,30 +82,26 @@ func (dst *pguint32) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src pguint32) EncodeText(w io.Writer) error { func (src pguint32) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
s := strconv.FormatUint(uint64(src.Uint), 10) _, err := io.WriteString(w, strconv.FormatUint(uint64(src.Uint), 10))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src pguint32) EncodeBinary(w io.Writer) error { func (src pguint32) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 4) _, err := pgio.WriteUint32(w, src.Uint)
if err != nil { return false, err
return err
}
_, err = pgio.WriteUint32(w, src.Uint)
return err
} }

View File

@ -120,15 +120,13 @@ func (dst *QChar) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src QChar) EncodeBinary(w io.Writer) error { func (src QChar) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, 1) return false, pgio.WriteByte(w, byte(src.Int))
if err != nil {
return nil
}
return pgio.WriteByte(w, byte(src.Int))
} }

View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"github.com/jackc/pgx/pgio"
) )
type Text struct { type Text struct {
@ -85,20 +83,18 @@ func (dst *Text) DecodeBinary(src []byte) error {
return dst.DecodeText(src) return dst.DecodeText(src)
} }
func (src Text) EncodeText(w io.Writer) error { func (src Text) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
_, err := pgio.WriteInt32(w, int32(len(src.String))) _, err := io.WriteString(w, src.String)
if err != nil { return false, err
return nil
}
_, err = io.WriteString(w, src.String)
return err
} }
func (src Text) EncodeBinary(w io.Writer) error { func (src Text) EncodeBinary(w io.Writer) (bool, error) {
return src.EncodeText(w) return src.EncodeText(w)
} }

View File

@ -1,112 +0,0 @@
package pgtype
import (
"bytes"
"errors"
"io"
"github.com/jackc/pgx/pgio"
)
// TextElementWriter is a wrapper that makes TextEncoders composable into other
// TextEncoders. TextEncoder first writes the length of the subsequent value.
// This is not necessary when the value is part of another value such as an
// array. TextElementWriter requires one int32 to be written first which it
// ignores. No other integer writes are valid.
type TextElementWriter struct {
w io.Writer
lengthHeaderIgnored bool
}
func NewTextElementWriter(w io.Writer) *TextElementWriter {
return &TextElementWriter{w: w}
}
func (w *TextElementWriter) WriteUint16(n uint16) (int, error) {
return 0, errors.New("WriteUint16 should never be called on TextElementWriter")
}
func (w *TextElementWriter) WriteUint32(n uint32) (int, error) {
if !w.lengthHeaderIgnored {
w.lengthHeaderIgnored = true
if int32(n) == -1 {
return io.WriteString(w.w, "NULL")
}
return 4, nil
}
return 0, errors.New("WriteUint32 should only be called once on TextElementWriter")
}
func (w *TextElementWriter) WriteUint64(n uint64) (int, error) {
if w.lengthHeaderIgnored {
return pgio.WriteUint64(w.w, n)
}
return 0, errors.New("WriteUint64 should never be called on TextElementWriter")
}
func (w *TextElementWriter) Write(buf []byte) (int, error) {
if w.lengthHeaderIgnored {
return w.w.Write(buf)
}
return 0, errors.New("int32 must be written first")
}
func (w *TextElementWriter) Reset() {
w.lengthHeaderIgnored = false
}
// TextElementReader is a wrapper that makes TextDecoders composable into other
// TextDecoders. TextEncoders first read the length of the subsequent value.
// This length value is not present when the value is part of another value such
// as an array. TextElementReader provides a substitute length value from the
// length of the string. No other integer reads are valid. Each time DecodeText
// is called with a TextElementReader as the source the TextElementReader must
// first have Reset called with the new element string data.
type TextElementReader struct {
buf *bytes.Buffer
lengthHeaderIgnored bool
}
func NewTextElementReader(r io.Reader) *TextElementReader {
return &TextElementReader{buf: &bytes.Buffer{}}
}
func (r *TextElementReader) ReadUint16() (uint16, error) {
return 0, errors.New("ReadUint16 should never be called on TextElementReader")
}
func (r *TextElementReader) ReadUint32() (uint32, error) {
if !r.lengthHeaderIgnored {
r.lengthHeaderIgnored = true
if r.buf.String() == "NULL" {
n32 := int32(-1)
return uint32(n32), nil
}
return uint32(r.buf.Len()), nil
}
return 0, errors.New("ReadUint32 should only be called once on TextElementReader")
}
func (r *TextElementReader) WriteUint64(n uint64) (int, error) {
return 0, errors.New("ReadUint64 should never be called on TextElementReader")
}
func (r *TextElementReader) Read(buf []byte) (int, error) {
if r.lengthHeaderIgnored {
return r.buf.Read(buf)
}
return 0, errors.New("int32 must be read first")
}
func (r *TextElementReader) Reset(s string) {
r.lengthHeaderIgnored = false
r.buf.Reset()
r.buf.WriteString(s)
}

View File

@ -152,26 +152,22 @@ func (dst *TextArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *TextArray) EncodeText(w io.Writer) error { func (src *TextArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -185,112 +181,112 @@ func (src *TextArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
if elem.Status == Null { null, err := elem.EncodeText(elemBuf)
_, err := io.WriteString(buf, `"NULL"`)
if err != nil { if err != nil {
return err return false, err
} }
} else if elem.String == "" { if null {
_, err := io.WriteString(buf, `""`) _, err = io.WriteString(w, `"NULL"`)
if err != nil { if err != nil {
return err return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
} }
} else { } else {
err = elem.EncodeText(textElementWriter) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *TextArray) EncodeBinary(w io.Writer) error { func (src *TextArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TextOID) return src.encodeBinary(w, TextOID)
} }
func (src *TextArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *TextArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -127,12 +127,15 @@ func (dst *Timestamp) DecodeBinary(src []byte) error {
// EncodeText writes the text encoding of src into w. If src.Time is not in // EncodeText writes the text encoding of src into w. If src.Time is not in
// the UTC time zone it returns an error. // the UTC time zone it returns an error.
func (src Timestamp) EncodeText(w io.Writer) error { func (src Timestamp) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if src.Time.Location() != time.UTC { if src.Time.Location() != time.UTC {
return fmt.Errorf("cannot encode non-UTC time into timestamp") return false, fmt.Errorf("cannot encode non-UTC time into timestamp")
} }
var s string var s string
@ -146,28 +149,21 @@ func (src Timestamp) EncodeText(w io.Writer) error {
s = "-infinity" s = "-infinity"
} }
_, err := pgio.WriteInt32(w, int32(len(s))) _, err := io.WriteString(w, s)
if err != nil { return false, err
return nil
}
_, err = w.Write([]byte(s))
return err
} }
// EncodeBinary writes the binary encoding of src into w. If src.Time is not in // EncodeBinary writes the binary encoding of src into w. If src.Time is not in
// the UTC time zone it returns an error. // the UTC time zone it returns an error.
func (src Timestamp) EncodeBinary(w io.Writer) error { func (src Timestamp) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if src.Time.Location() != time.UTC { if src.Time.Location() != time.UTC {
return fmt.Errorf("cannot encode non-UTC time into timestamp") return false, fmt.Errorf("cannot encode non-UTC time into timestamp")
}
_, err := pgio.WriteInt32(w, 8)
if err != nil {
return err
} }
var microsecSinceY2K int64 var microsecSinceY2K int64
@ -181,6 +177,6 @@ func (src Timestamp) EncodeBinary(w io.Writer) error {
microsecSinceY2K = negativeInfinityMicrosecondOffset microsecSinceY2K = negativeInfinityMicrosecondOffset
} }
_, err = pgio.WriteInt64(w, microsecSinceY2K) _, err := pgio.WriteInt64(w, microsecSinceY2K)
return err return false, err
} }

View File

@ -153,26 +153,22 @@ func (dst *TimestampArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *TimestampArray) EncodeText(w io.Writer) error { func (src *TimestampArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -186,100 +182,112 @@ func (src *TimestampArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *TimestampArray) EncodeBinary(w io.Writer) error { func (src *TimestampArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TimestampOID) return src.encodeBinary(w, TimestampOID)
} }
func (src *TimestampArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *TimestampArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -131,9 +131,12 @@ func (dst *Timestamptz) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src Timestamptz) EncodeText(w io.Writer) error { func (src Timestamptz) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var s string var s string
@ -147,23 +150,16 @@ func (src Timestamptz) EncodeText(w io.Writer) error {
s = "-infinity" s = "-infinity"
} }
_, err := pgio.WriteInt32(w, int32(len(s))) _, err := io.WriteString(w, s)
if err != nil { return false, err
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src Timestamptz) EncodeBinary(w io.Writer) error { func (src Timestamptz) EncodeBinary(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
} return true, nil
case Undefined:
_, err := pgio.WriteInt32(w, 8) return false, errUndefined
if err != nil {
return err
} }
var microsecSinceY2K int64 var microsecSinceY2K int64
@ -177,6 +173,6 @@ func (src Timestamptz) EncodeBinary(w io.Writer) error {
microsecSinceY2K = negativeInfinityMicrosecondOffset microsecSinceY2K = negativeInfinityMicrosecondOffset
} }
_, err = pgio.WriteInt64(w, microsecSinceY2K) _, err := pgio.WriteInt64(w, microsecSinceY2K)
return err return false, err
} }

View File

@ -153,26 +153,22 @@ func (dst *TimestamptzArray) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *TimestamptzArray) EncodeText(w io.Writer) error { func (src *TimestamptzArray) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -186,100 +182,112 @@ func (src *TimestamptzArray) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `NULL`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *TimestamptzArray) EncodeBinary(w io.Writer) error { func (src *TimestamptzArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TimestamptzOID) return src.encodeBinary(w, TimestamptzOID)
} }
func (src *TimestamptzArray) encodeBinary(w io.Writer, elementOID int32) error { func (src *TimestamptzArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -1,9 +0,0 @@
DecodeText and DecodeBinary take []byte instead of io.Reader
EncodeText and EncodeBinary do not write size
Add Nullable interface with IsNull() and SetNull()
The above would keep types from needing to worry about writing their own size. Could make EncodeText and DecodeText easier to use with sql.Scanner and driver.Valuer. SetNull() could be removed as DecodeText and DecodeBinary could interpret a nil slice as null.
EncodeText and EncodeBinary could return (null bool, err error). That would finish removing Nullable interface.
Also, consider whether arrays and ranges could be represented as generic data types or more common code could be extracted instead of using code generation.

View File

@ -151,26 +151,22 @@ func (dst *<%= pgtype_array_type %>) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src *<%= pgtype_array_type %>) EncodeText(w io.Writer) error { func (src *<%= pgtype_array_type %>) EncodeText(w io.Writer) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
if len(src.Dimensions) == 0 { if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2) _, err := io.WriteString(w, "{}")
if err != nil { return false, err
return err
} }
_, err = w.Write([]byte("{}")) err := EncodeTextArrayDimensions(w, src.Dimensions)
return err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
if err != nil { if err != nil {
return err return false, err
} }
// dimElemCounts is the multiples of elements that each array lies on. For // dimElemCounts is the multiples of elements that each array lies on. For
@ -184,100 +180,112 @@ func (src *<%= pgtype_array_type %>) EncodeText(w io.Writer) error {
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
} }
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements { for i, elem := range src.Elements {
if i > 0 { if i > 0 {
err = pgio.WriteByte(buf, ',') err = pgio.WriteByte(w, ',')
if err != nil { if err != nil {
return err return false, err
} }
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if i%dec == 0 { if i%dec == 0 {
err = pgio.WriteByte(buf, '{') err = pgio.WriteByte(w, '{')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
textElementWriter.Reset() elemBuf := &bytes.Buffer{}
err = elem.EncodeText(textElementWriter) null, err := elem.EncodeText(elemBuf)
if err != nil { if err != nil {
return err return false, err
}
if null {
_, err = io.WriteString(w, `<%= text_null %>`)
if err != nil {
return false, err
}
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return false, err
}
} else {
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
if (i+1)%dec == 0 { if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}') err = pgio.WriteByte(w, '}')
if err != nil { if err != nil {
return err return false, err
} }
} }
} }
} }
_, err = pgio.WriteInt32(w, int32(buf.Len())) return false, nil
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
} }
func (src *<%= pgtype_array_type %>) EncodeBinary(w io.Writer) error { func (src *<%= pgtype_array_type %>) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, <%= element_oid %>) return src.encodeBinary(w, <%= element_oid %>)
} }
func (src *<%= pgtype_array_type %>) encodeBinary(w io.Writer, elementOID int32) error { func (src *<%= pgtype_array_type %>) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
if done, err := encodeNotPresent(w, src.Status); done { switch src.Status {
return err case Null:
return true, nil
case Undefined:
return false, errUndefined
} }
var arrayHeader ArrayHeader arrayHeader := ArrayHeader{
ElementOID: elementOID,
Dimensions: src.Dimensions,
}
for i := range src.Elements {
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
break
}
}
err := arrayHeader.EncodeBinary(w)
if err != nil {
return false, err
}
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
elemBuf := &bytes.Buffer{} elemBuf := &bytes.Buffer{}
for i := range src.Elements { for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf) elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil { if err != nil {
return err return false, err
} }
if src.Elements[i].Status == Null { if null {
arrayHeader.ContainsNull = true _, err = pgio.WriteInt32(w, -1)
}
}
arrayHeader.ElementOID = elementOID
arrayHeader.Dimensions = src.Dimensions
// TODO - consider how to avoid having to buffer array before writing length -
// or how not pay allocations for the byte order conversions.
headerBuf := &bytes.Buffer{}
err := arrayHeader.EncodeBinary(headerBuf)
if err != nil { if err != nil {
return err return false, err
} }
} else {
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len())) _, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil { if err != nil {
return err return false, err
} }
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w) _, err = elemBuf.WriteTo(w)
if err != nil { if err != nil {
return err return false, err
}
}
} }
return err return false, err
} }

View File

@ -1,11 +1,11 @@
erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]uint16 element_oid=Int2OID typed_array.go.erb > int2array.go erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]uint16 element_oid=Int2OID text_null=NULL typed_array.go.erb > int2array.go
erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int32,[]uint32 element_oid=Int4OID typed_array.go.erb > int4array.go erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int32,[]uint32 element_oid=Int4OID text_null=NULL typed_array.go.erb > int4array.go
erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int64,[]uint64 element_oid=Int8OID typed_array.go.erb > int8array.go erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int64,[]uint64 element_oid=Int8OID text_null=NULL typed_array.go.erb > int8array.go
erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool element_oid=BoolOID typed_array.go.erb > boolarray.go erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool element_oid=BoolOID text_null=NULL typed_array.go.erb > boolarray.go
erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time element_oid=DateOID typed_array.go.erb > datearray.go erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time element_oid=DateOID text_null=NULL typed_array.go.erb > datearray.go
erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time element_oid=TimestamptzOID typed_array.go.erb > timestamptzarray.go erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time element_oid=TimestamptzOID text_null=NULL typed_array.go.erb > timestamptzarray.go
erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time element_oid=TimestampOID typed_array.go.erb > timestamparray.go erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time element_oid=TimestampOID text_null=NULL typed_array.go.erb > timestamparray.go
erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32 element_oid=Float4OID typed_array.go.erb > float4array.go erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32 element_oid=Float4OID text_null=NULL typed_array.go.erb > float4array.go
erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64 element_oid=Float8OID typed_array.go.erb > float8array.go erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64 element_oid=Float8OID text_null=NULL typed_array.go.erb > float8array.go
erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP element_oid=InetOID typed_array.go.erb > inetarray.go erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP element_oid=InetOID text_null=NULL typed_array.go.erb > inetarray.go
erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string element_oid=TextOID typed_array.go.erb > textarray.go erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string element_oid=TextOID text_null='"NULL"' typed_array.go.erb > textarray.go

View File

@ -22,10 +22,10 @@ func (dst *VarcharArray) DecodeBinary(src []byte) error {
return (*TextArray)(dst).DecodeBinary(src) return (*TextArray)(dst).DecodeBinary(src)
} }
func (src *VarcharArray) EncodeText(w io.Writer) error { func (src *VarcharArray) EncodeText(w io.Writer) (bool, error) {
return (*TextArray)(src).EncodeText(w) return (*TextArray)(src).EncodeText(w)
} }
func (src *VarcharArray) EncodeBinary(w io.Writer) error { func (src *VarcharArray) EncodeBinary(w io.Writer) (bool, error) {
return (*TextArray)(src).encodeBinary(w, VarcharOID) return (*TextArray)(src).encodeBinary(w, VarcharOID)
} }

View File

@ -41,10 +41,10 @@ func (dst *XID) DecodeBinary(src []byte) error {
return (*pguint32)(dst).DecodeBinary(src) return (*pguint32)(dst).DecodeBinary(src)
} }
func (src XID) EncodeText(w io.Writer) error { func (src XID) EncodeText(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeText(w) return (pguint32)(src).EncodeText(w)
} }
func (src XID) EncodeBinary(w io.Writer) error { func (src XID) EncodeBinary(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeBinary(w) return (pguint32)(src).EncodeBinary(w)
} }

120
values.go
View File

@ -408,7 +408,10 @@ func (n NullInt16) Encode(w *WriteBuf, oid OID) error {
return nil return nil
} }
return pgtype.Int2{Int: n.Int16, Status: pgtype.Present}.EncodeBinary(w) w.WriteInt32(2)
_, err := pgtype.Int2{Int: n.Int16, Status: pgtype.Present}.EncodeBinary(w)
return err
} }
// NullInt32 represents an integer that may be null. NullInt32 implements the // NullInt32 represents an integer that may be null. NullInt32 implements the
@ -447,7 +450,10 @@ func (n NullInt32) Encode(w *WriteBuf, oid OID) error {
return nil return nil
} }
return pgtype.Int4{Int: n.Int32, Status: pgtype.Present}.EncodeBinary(w) w.WriteInt32(4)
_, err := pgtype.Int4{Int: n.Int32, Status: pgtype.Present}.EncodeBinary(w)
return err
} }
// OID (Object Identifier Type) is, according to https://www.postgresql.org/docs/current/static/datatype-oid.html, // OID (Object Identifier Type) is, according to https://www.postgresql.org/docs/current/static/datatype-oid.html,
@ -484,24 +490,14 @@ func (dst *OID) DecodeBinary(src []byte) error {
return nil return nil
} }
func (src OID) EncodeText(w io.Writer) error { func (src OID) EncodeText(w io.Writer) (bool, error) {
s := strconv.FormatUint(uint64(src), 10) _, err := io.WriteString(w, strconv.FormatUint(uint64(src), 10))
_, err := pgio.WriteInt32(w, int32(len(s))) return false, err
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
} }
func (src OID) EncodeBinary(w io.Writer) error { func (src OID) EncodeBinary(w io.Writer) (bool, error) {
_, err := pgio.WriteInt32(w, 4) _, err := pgio.WriteUint32(w, uint32(src))
if err != nil { return false, err
return err
}
_, err = pgio.WriteUint32(w, uint32(src))
return err
} }
// Tid is PostgreSQL's Tuple Identifier type. // Tid is PostgreSQL's Tuple Identifier type.
@ -595,7 +591,10 @@ func (n NullInt64) Encode(w *WriteBuf, oid OID) error {
return nil return nil
} }
return pgtype.Int8{Int: n.Int64, Status: pgtype.Present}.EncodeBinary(w) w.WriteInt32(8)
_, err := pgtype.Int8{Int: n.Int64, Status: pgtype.Present}.EncodeBinary(w)
return err
} }
// NullBool represents an bool that may be null. NullBool implements the Scanner // NullBool represents an bool that may be null. NullBool implements the Scanner
@ -634,7 +633,10 @@ func (n NullBool) Encode(w *WriteBuf, oid OID) error {
return nil return nil
} }
return encodeBool(w, oid, n.Bool) w.WriteInt32(1)
_, err := pgtype.Bool{Bool: n.Bool, Status: pgtype.Present}.EncodeBinary(w)
return err
} }
// NullTime represents an time.Time that may be null. NullTime implements the // NullTime represents an time.Time that may be null. NullTime implements the
@ -834,9 +836,31 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
case Encoder: case Encoder:
return arg.Encode(wbuf, oid) return arg.Encode(wbuf, oid)
case pgtype.BinaryEncoder: case pgtype.BinaryEncoder:
return arg.EncodeBinary(wbuf) buf := &bytes.Buffer{}
null, err := arg.EncodeBinary(buf)
if err != nil {
return err
}
if null {
wbuf.WriteInt32(-1)
} else {
wbuf.WriteInt32(int32(buf.Len()))
wbuf.WriteBytes(buf.Bytes())
}
return nil
case pgtype.TextEncoder: case pgtype.TextEncoder:
return arg.EncodeText(wbuf) buf := &bytes.Buffer{}
null, err := arg.EncodeText(buf)
if err != nil {
return err
}
if null {
wbuf.WriteInt32(-1)
} else {
wbuf.WriteInt32(int32(buf.Len()))
wbuf.WriteBytes(buf.Bytes())
}
return nil
case driver.Valuer: case driver.Valuer:
v, err := arg.Value() v, err := arg.Value()
if err != nil { if err != nil {
@ -876,7 +900,19 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
if err != nil { if err != nil {
return err return err
} }
return value.(pgtype.BinaryEncoder).EncodeBinary(wbuf)
buf := &bytes.Buffer{}
null, err := value.(pgtype.BinaryEncoder).EncodeBinary(buf)
if err != nil {
return err
}
if null {
wbuf.WriteInt32(-1)
} else {
wbuf.WriteInt32(int32(buf.Len()))
wbuf.WriteBytes(buf.Bytes())
}
return nil
} }
switch arg := arg.(type) { switch arg := arg.(type) {
@ -1026,15 +1062,6 @@ func decodeBool(vr *ValueReader) bool {
return b.Bool return b.Bool
} }
func encodeBool(w *WriteBuf, oid OID, value bool) error {
if oid != BoolOID {
return fmt.Errorf("cannot encode Go %s into oid %d", "bool", oid)
}
b := pgtype.Bool{Bool: value, Status: pgtype.Present}
return b.EncodeBinary(w)
}
func decodeInt(vr *ValueReader) int64 { func decodeInt(vr *ValueReader) int64 {
switch vr.Type().DataType { switch vr.Type().DataType {
case Int2OID: case Int2OID:
@ -1461,14 +1488,39 @@ func encodeTime(w *WriteBuf, oid OID, value time.Time) error {
if err != nil { if err != nil {
return err return err
} }
return d.EncodeBinary(w)
buf := &bytes.Buffer{}
null, err := d.EncodeBinary(buf)
if err != nil {
return err
}
if null {
w.WriteInt32(-1)
} else {
w.WriteInt32(int32(buf.Len()))
w.WriteBytes(buf.Bytes())
}
return nil
case TimestampTzOID, TimestampOID: case TimestampTzOID, TimestampOID:
var t pgtype.Timestamptz var t pgtype.Timestamptz
err := t.ConvertFrom(value) err := t.ConvertFrom(value)
if err != nil { if err != nil {
return err return err
} }
return t.EncodeBinary(w)
buf := &bytes.Buffer{}
null, err := t.EncodeBinary(buf)
if err != nil {
return err
}
if null {
w.WriteInt32(-1)
} else {
w.WriteInt32(int32(buf.Len()))
w.WriteBytes(buf.Bytes())
}
return nil
default: default:
return fmt.Errorf("cannot encode %s into oid %v", "time.Time", oid) return fmt.Errorf("cannot encode %s into oid %v", "time.Time", oid)
} }