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

View File

@ -152,26 +152,22 @@ func (dst *BoolArray) DecodeBinary(src []byte) error {
return nil
}
func (src *BoolArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *BoolArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *BoolArray) EncodeBinary(w io.Writer) error {
func (src *BoolArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, BoolOID)
}
func (src *BoolArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *BoolArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -38,10 +38,10 @@ func (dst *CID) DecodeBinary(src []byte) error {
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)
}
func (src CID) EncodeBinary(w io.Writer) error {
func (src CID) EncodeBinary(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeBinary(w)
}

View File

@ -22,10 +22,10 @@ func (dst *CidrArray) DecodeBinary(src []byte) error {
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)
}
func (src *CidrArray) EncodeBinary(w io.Writer) error {
func (src *CidrArray) EncodeBinary(w io.Writer) (bool, error) {
return (*InetArray)(src).encodeBinary(w, CidrOID)
}

View File

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

View File

@ -153,26 +153,22 @@ func (dst *DateArray) DecodeBinary(src []byte) error {
return nil
}
func (src *DateArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *DateArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *DateArray) EncodeBinary(w io.Writer) error {
func (src *DateArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, DateOID)
}
func (src *DateArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *DateArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -152,26 +152,22 @@ func (dst *Float4Array) DecodeBinary(src []byte) error {
return nil
}
func (src *Float4Array) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Float4Array) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *Float4Array) EncodeBinary(w io.Writer) error {
func (src *Float4Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Float4OID)
}
func (src *Float4Array) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Float4Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -152,26 +152,22 @@ func (dst *Float8Array) DecodeBinary(src []byte) error {
return nil
}
func (src *Float8Array) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Float8Array) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *Float8Array) EncodeBinary(w io.Writer) error {
func (src *Float8Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Float8OID)
}
func (src *Float8Array) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Float8Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

@ -144,61 +144,55 @@ func (dst *Inet) DecodeBinary(src []byte) error {
return nil
}
func (src Inet) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src Inet) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
s := src.IPNet.String()
_, err := pgio.WriteInt32(w, int32(len(s)))
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
_, err := io.WriteString(w, src.IPNet.String())
return false, err
}
// EncodeBinary encodes src into w.
func (src Inet) EncodeBinary(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src Inet) EncodeBinary(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
var size int32
var family byte
switch len(src.IPNet.IP) {
case net.IPv4len:
size = 8
family = defaultAFInet
case net.IPv6len:
size = 20
family = defaultAFInet6
default:
return fmt.Errorf("Unexpected IP length: %v", len(src.IPNet.IP))
}
if _, err := pgio.WriteInt32(w, size); err != nil {
return err
return false, fmt.Errorf("Unexpected IP length: %v", len(src.IPNet.IP))
}
if err := pgio.WriteByte(w, family); err != nil {
return err
return false, err
}
ones, _ := src.IPNet.Mask.Size()
if err := pgio.WriteByte(w, byte(ones)); err != nil {
return err
return false, err
}
// is_cidr is ignored on server
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 {
return err
return false, err
}
_, 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
}
func (src *InetArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *InetArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *InetArray) EncodeBinary(w io.Writer) error {
func (src *InetArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, InetOID)
}
func (src *InetArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *InetArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -183,26 +183,22 @@ func (dst *Int2Array) DecodeBinary(src []byte) error {
return nil
}
func (src *Int2Array) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int2Array) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *Int2Array) EncodeBinary(w io.Writer) error {
func (src *Int2Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int2OID)
}
func (src *Int2Array) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int2Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -183,26 +183,22 @@ func (dst *Int4Array) DecodeBinary(src []byte) error {
return nil
}
func (src *Int4Array) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int4Array) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *Int4Array) EncodeBinary(w io.Writer) error {
func (src *Int4Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int4OID)
}
func (src *Int4Array) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int4Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -183,26 +183,22 @@ func (dst *Int8Array) DecodeBinary(src []byte) error {
return nil
}
func (src *Int8Array) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int8Array) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *Int8Array) EncodeBinary(w io.Writer) error {
func (src *Int8Array) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, Int8OID)
}
func (src *Int8Array) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *Int8Array) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

@ -35,10 +35,10 @@ func (dst *Name) DecodeBinary(src []byte) error {
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)
}
func (src Name) EncodeBinary(w io.Writer) error {
func (src Name) EncodeBinary(w io.Writer) (bool, error) {
return (Text)(src).EncodeBinary(w)
}

View File

@ -32,10 +32,10 @@ func (dst *OID) DecodeBinary(src []byte) error {
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)
}
func (src OID) EncodeBinary(w io.Writer) error {
func (src OID) EncodeBinary(w io.Writer) (bool, error) {
return (pguint32)(src).EncodeBinary(w)
}

View File

@ -3,8 +3,6 @@ package pgtype
import (
"errors"
"io"
"github.com/jackc/pgx/pgio"
)
// PostgreSQL oids for common types
@ -81,23 +79,24 @@ type TextDecoder interface {
DecodeText(src []byte) error
}
// BinaryEncoder is implemented by types that can encode themselves into the
// PostgreSQL binary wire format.
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 {
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")
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
}
func (f forceTextEncoder) EncodeText(w io.Writer) error {
func (f forceTextEncoder) EncodeText(w io.Writer) (bool, error) {
return f.e.EncodeText(w)
}
@ -68,7 +68,7 @@ type forceBinaryEncoder struct {
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)
}

View File

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

View File

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

View File

@ -4,8 +4,6 @@ import (
"fmt"
"io"
"reflect"
"github.com/jackc/pgx/pgio"
)
type Text struct {
@ -85,20 +83,18 @@ func (dst *Text) DecodeBinary(src []byte) error {
return dst.DecodeText(src)
}
func (src Text) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src Text) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
_, err := pgio.WriteInt32(w, int32(len(src.String)))
if err != nil {
return nil
}
_, err = io.WriteString(w, src.String)
return err
_, err := io.WriteString(w, src.String)
return false, err
}
func (src Text) EncodeBinary(w io.Writer) error {
func (src Text) EncodeBinary(w io.Writer) (bool, error) {
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
}
func (src *TextArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TextArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
if elem.Status == Null {
_, err := io.WriteString(buf, `"NULL"`)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
if err != nil {
return false, err
}
if null {
_, err = io.WriteString(w, `"NULL"`)
if err != nil {
return err
return false, err
}
} else if elem.String == "" {
_, err := io.WriteString(buf, `""`)
} else if elemBuf.Len() == 0 {
_, err = io.WriteString(w, `""`)
if err != nil {
return err
return false, err
}
} else {
err = elem.EncodeText(textElementWriter)
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *TextArray) EncodeBinary(w io.Writer) error {
func (src *TextArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TextOID)
}
func (src *TextArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TextArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return 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
// the UTC time zone it returns an error.
func (src Timestamp) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src Timestamp) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
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
@ -146,28 +149,21 @@ func (src Timestamp) EncodeText(w io.Writer) error {
s = "-infinity"
}
_, err := pgio.WriteInt32(w, int32(len(s)))
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
_, err := io.WriteString(w, s)
return false, err
}
// EncodeBinary writes the binary encoding of src into w. If src.Time is not in
// the UTC time zone it returns an error.
func (src Timestamp) EncodeBinary(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src Timestamp) EncodeBinary(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if src.Time.Location() != time.UTC {
return fmt.Errorf("cannot encode non-UTC time into timestamp")
}
_, err := pgio.WriteInt32(w, 8)
if err != nil {
return err
return false, fmt.Errorf("cannot encode non-UTC time into timestamp")
}
var microsecSinceY2K int64
@ -181,6 +177,6 @@ func (src Timestamp) EncodeBinary(w io.Writer) error {
microsecSinceY2K = negativeInfinityMicrosecondOffset
}
_, err = pgio.WriteInt64(w, microsecSinceY2K)
return err
_, err := pgio.WriteInt64(w, microsecSinceY2K)
return false, err
}

View File

@ -153,26 +153,22 @@ func (dst *TimestampArray) DecodeBinary(src []byte) error {
return nil
}
func (src *TimestampArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TimestampArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *TimestampArray) EncodeBinary(w io.Writer) error {
func (src *TimestampArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TimestampOID)
}
func (src *TimestampArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TimestampArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return err
}
return err
return false, err
}

View File

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

View File

@ -153,26 +153,22 @@ func (dst *TimestamptzArray) DecodeBinary(src []byte) error {
return nil
}
func (src *TimestamptzArray) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TimestamptzArray) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
func (src *TimestamptzArray) EncodeBinary(w io.Writer) error {
func (src *TimestamptzArray) EncodeBinary(w io.Writer) (bool, error) {
return src.encodeBinary(w, TimestamptzOID)
}
func (src *TimestamptzArray) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *TimestamptzArray) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return 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
}
func (src *<%= pgtype_array_type %>) EncodeText(w io.Writer) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *<%= pgtype_array_type %>) EncodeText(w io.Writer) (bool, error) {
switch src.Status {
case Null:
return true, nil
case Undefined:
return false, errUndefined
}
if len(src.Dimensions) == 0 {
_, err := pgio.WriteInt32(w, 2)
if err != nil {
return err
}
_, err = w.Write([]byte("{}"))
return err
_, err := io.WriteString(w, "{}")
return false, err
}
buf := &bytes.Buffer{}
err := EncodeTextArrayDimensions(buf, src.Dimensions)
err := EncodeTextArrayDimensions(w, src.Dimensions)
if err != nil {
return err
return false, err
}
// 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]
}
textElementWriter := NewTextElementWriter(buf)
for i, elem := range src.Elements {
if i > 0 {
err = pgio.WriteByte(buf, ',')
err = pgio.WriteByte(w, ',')
if err != nil {
return err
return false, err
}
}
for _, dec := range dimElemCounts {
if i%dec == 0 {
err = pgio.WriteByte(buf, '{')
err = pgio.WriteByte(w, '{')
if err != nil {
return err
return false, err
}
}
}
textElementWriter.Reset()
err = elem.EncodeText(textElementWriter)
elemBuf := &bytes.Buffer{}
null, err := elem.EncodeText(elemBuf)
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 {
if (i+1)%dec == 0 {
err = pgio.WriteByte(buf, '}')
err = pgio.WriteByte(w, '}')
if err != nil {
return err
return false, err
}
}
}
}
_, err = pgio.WriteInt32(w, int32(buf.Len()))
if err != nil {
return err
}
_, err = buf.WriteTo(w)
return err
return false, nil
}
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 %>)
}
func (src *<%= pgtype_array_type %>) encodeBinary(w io.Writer, elementOID int32) error {
if done, err := encodeNotPresent(w, src.Status); done {
return err
func (src *<%= pgtype_array_type %>) encodeBinary(w io.Writer, elementOID int32) (bool, error) {
switch src.Status {
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{}
for i := range src.Elements {
err := src.Elements[i].EncodeBinary(elemBuf)
elemBuf.Reset()
null, err := src.Elements[i].EncodeBinary(elemBuf)
if err != nil {
return err
return false, err
}
if src.Elements[i].Status == Null {
arrayHeader.ContainsNull = true
if null {
_, err = pgio.WriteInt32(w, -1)
if err != nil {
return false, err
}
} else {
_, err = pgio.WriteInt32(w, int32(elemBuf.Len()))
if err != nil {
return false, err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return false, err
}
}
}
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 {
return err
}
_, err = pgio.WriteInt32(w, int32(headerBuf.Len()+elemBuf.Len()))
if err != nil {
return err
}
_, err = headerBuf.WriteTo(w)
if err != nil {
return err
}
_, err = elemBuf.WriteTo(w)
if err != nil {
return 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=Int4Array pgtype_element_type=Int4 go_array_types=[]int32,[]uint32 element_oid=Int4OID 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=BoolArray pgtype_element_type=Bool go_array_types=[]bool element_oid=BoolOID 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=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time element_oid=TimestamptzOID 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=Float4Array pgtype_element_type=Float4 go_array_types=[]float32 element_oid=Float4OID 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=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=TextArray pgtype_element_type=Text go_array_types=[]string element_oid=TextOID typed_array.go.erb > textarray.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 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 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 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 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 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 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 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 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 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 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)
}
func (src *VarcharArray) EncodeText(w io.Writer) error {
func (src *VarcharArray) EncodeText(w io.Writer) (bool, error) {
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)
}

View File

@ -41,10 +41,10 @@ func (dst *XID) DecodeBinary(src []byte) error {
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)
}
func (src XID) EncodeBinary(w io.Writer) error {
func (src XID) EncodeBinary(w io.Writer) (bool, error) {
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 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
@ -447,7 +450,10 @@ func (n NullInt32) Encode(w *WriteBuf, oid OID) error {
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,
@ -484,24 +490,14 @@ func (dst *OID) DecodeBinary(src []byte) error {
return nil
}
func (src OID) EncodeText(w io.Writer) error {
s := strconv.FormatUint(uint64(src), 10)
_, err := pgio.WriteInt32(w, int32(len(s)))
if err != nil {
return nil
}
_, err = w.Write([]byte(s))
return err
func (src OID) EncodeText(w io.Writer) (bool, error) {
_, err := io.WriteString(w, strconv.FormatUint(uint64(src), 10))
return false, err
}
func (src OID) EncodeBinary(w io.Writer) error {
_, err := pgio.WriteInt32(w, 4)
if err != nil {
return err
}
_, err = pgio.WriteUint32(w, uint32(src))
return err
func (src OID) EncodeBinary(w io.Writer) (bool, error) {
_, err := pgio.WriteUint32(w, uint32(src))
return false, err
}
// Tid is PostgreSQL's Tuple Identifier type.
@ -595,7 +591,10 @@ func (n NullInt64) Encode(w *WriteBuf, oid OID) error {
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
@ -634,7 +633,10 @@ func (n NullBool) Encode(w *WriteBuf, oid OID) error {
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
@ -834,9 +836,31 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
case Encoder:
return arg.Encode(wbuf, oid)
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:
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:
v, err := arg.Value()
if err != nil {
@ -876,7 +900,19 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
if err != nil {
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) {
@ -1026,15 +1062,6 @@ func decodeBool(vr *ValueReader) 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 {
switch vr.Type().DataType {
case Int2OID:
@ -1461,14 +1488,39 @@ func encodeTime(w *WriteBuf, oid OID, value time.Time) error {
if err != nil {
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:
var t pgtype.Timestamptz
err := t.ConvertFrom(value)
if err != nil {
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:
return fmt.Errorf("cannot encode %s into oid %v", "time.Time", oid)
}