mirror of https://github.com/jackc/pgx.git
Add text to pgtype
parent
b1fc8109db
commit
fa57904d6b
4
conn.go
4
conn.go
|
@ -286,10 +286,14 @@ func (c *Conn) connect(config ConnConfig, network, address string, tlsConfig *tl
|
||||||
Int4OID: &pgtype.Int4{},
|
Int4OID: &pgtype.Int4{},
|
||||||
Int8ArrayOID: &pgtype.Int8Array{},
|
Int8ArrayOID: &pgtype.Int8Array{},
|
||||||
Int8OID: &pgtype.Int8{},
|
Int8OID: &pgtype.Int8{},
|
||||||
|
TextArrayOID: &pgtype.TextArray{},
|
||||||
|
TextOID: &pgtype.Text{},
|
||||||
TimestampArrayOID: &pgtype.TimestampArray{},
|
TimestampArrayOID: &pgtype.TimestampArray{},
|
||||||
TimestampOID: &pgtype.Timestamp{},
|
TimestampOID: &pgtype.Timestamp{},
|
||||||
TimestampTzArrayOID: &pgtype.TimestamptzArray{},
|
TimestampTzArrayOID: &pgtype.TimestamptzArray{},
|
||||||
TimestampTzOID: &pgtype.Timestamptz{},
|
TimestampTzOID: &pgtype.Timestamptz{},
|
||||||
|
VarcharArrayOID: &pgtype.VarcharArray{},
|
||||||
|
VarcharOID: &pgtype.Text{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if tlsConfig != nil {
|
if tlsConfig != nil {
|
||||||
|
|
|
@ -40,6 +40,13 @@ func TestParseUntypedTextArray(t *testing.T) {
|
||||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: `{""}`,
|
||||||
|
result: pgtype.UntypedTextArray{
|
||||||
|
Elements: []string{""},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
source: `{"He said, \"Hello.\""}`,
|
source: `{"He said, \"Hello.\""}`,
|
||||||
result: pgtype.UntypedTextArray{
|
result: pgtype.UntypedTextArray{
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (src *Bool) AssignTo(dst interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (src *BoolArray) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -85,6 +85,25 @@ func underlyingBoolType(val interface{}) (interface{}, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// underlyingStringType gets the underlying type that can be converted to String
|
||||||
|
func underlyingStringType(val interface{}) (interface{}, bool) {
|
||||||
|
refVal := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
switch refVal.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if refVal.IsNil() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
convVal := refVal.Elem().Interface()
|
||||||
|
return convVal, true
|
||||||
|
case reflect.String:
|
||||||
|
convVal := refVal.String()
|
||||||
|
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
// underlyingPtrType dereferences a pointer
|
// underlyingPtrType dereferences a pointer
|
||||||
func underlyingPtrType(val interface{}) (interface{}, bool) {
|
func underlyingPtrType(val interface{}) (interface{}, bool) {
|
||||||
refVal := reflect.ValueOf(val)
|
refVal := reflect.ValueOf(val)
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (src *DateArray) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (src *Float4Array) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (src *Float8Array) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (src *InetArray) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (src *Int2Array) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (src *Int4Array) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (src *Int8Array) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test for renamed types
|
// Test for renamed types
|
||||||
|
type _string string
|
||||||
type _bool bool
|
type _bool bool
|
||||||
type _int8 int8
|
type _int8 int8
|
||||||
type _int16 int16
|
type _int16 int16
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
package pgtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgio"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Text struct {
|
||||||
|
String string
|
||||||
|
Status Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *Text) ConvertFrom(src interface{}) error {
|
||||||
|
switch value := src.(type) {
|
||||||
|
case Text:
|
||||||
|
*dst = value
|
||||||
|
case string:
|
||||||
|
*dst = Text{String: value, Status: Present}
|
||||||
|
case *string:
|
||||||
|
if value == nil {
|
||||||
|
*dst = Text{Status: Null}
|
||||||
|
} else {
|
||||||
|
*dst = Text{String: *value, Status: Present}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if originalSrc, ok := underlyingStringType(src); ok {
|
||||||
|
return dst.ConvertFrom(originalSrc)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot convert %v to Text", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *Text) AssignTo(dst interface{}) error {
|
||||||
|
switch v := dst.(type) {
|
||||||
|
case *string:
|
||||||
|
if src.Status != Present {
|
||||||
|
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||||
|
}
|
||||||
|
*v = src.String
|
||||||
|
default:
|
||||||
|
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||||
|
el := v.Elem()
|
||||||
|
switch el.Kind() {
|
||||||
|
// if dst is a pointer to pointer, strip the pointer and try again
|
||||||
|
case reflect.Ptr:
|
||||||
|
if src.Status == Null {
|
||||||
|
el.Set(reflect.Zero(el.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if el.IsNil() {
|
||||||
|
// allocate destination
|
||||||
|
el.Set(reflect.New(el.Type().Elem()))
|
||||||
|
}
|
||||||
|
return src.AssignTo(el.Interface())
|
||||||
|
case reflect.String:
|
||||||
|
if src.Status != Present {
|
||||||
|
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||||
|
}
|
||||||
|
el.SetString(src.String)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *Text) DecodeText(r io.Reader) error {
|
||||||
|
size, err := pgio.ReadInt32(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if size == -1 {
|
||||||
|
*dst = Text{Status: Null}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, int(size))
|
||||||
|
_, err = r.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = Text{String: string(buf), Status: Present}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *Text) DecodeBinary(r io.Reader) error {
|
||||||
|
return dst.DecodeText(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src Text) EncodeText(w io.Writer) error {
|
||||||
|
if done, err := encodeNotPresent(w, src.Status); done {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := pgio.WriteInt32(w, int32(len(src.String)))
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.WriteString(w, src.String)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src Text) EncodeBinary(w io.Writer) error {
|
||||||
|
return src.EncodeText(w)
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package pgtype_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextTranscode(t *testing.T) {
|
||||||
|
for _, pgTypeName := range []string{"text", "varchar"} {
|
||||||
|
testSuccessfulTranscode(t, pgTypeName, []interface{}{
|
||||||
|
pgtype.Text{String: "", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "foo", Status: pgtype.Present},
|
||||||
|
pgtype.Text{Status: pgtype.Null},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextConvertFrom(t *testing.T) {
|
||||||
|
successfulTests := []struct {
|
||||||
|
source interface{}
|
||||||
|
result pgtype.Text
|
||||||
|
}{
|
||||||
|
{source: pgtype.Text{String: "foo", Status: pgtype.Present}, result: pgtype.Text{String: "foo", Status: pgtype.Present}},
|
||||||
|
{source: "foo", result: pgtype.Text{String: "foo", Status: pgtype.Present}},
|
||||||
|
{source: _string("bar"), result: pgtype.Text{String: "bar", Status: pgtype.Present}},
|
||||||
|
{source: (*string)(nil), result: pgtype.Text{Status: pgtype.Null}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range successfulTests {
|
||||||
|
var d pgtype.Text
|
||||||
|
err := d.ConvertFrom(tt.source)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d != tt.result {
|
||||||
|
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextAssignTo(t *testing.T) {
|
||||||
|
var s string
|
||||||
|
var ps *string
|
||||||
|
|
||||||
|
simpleTests := []struct {
|
||||||
|
src pgtype.Text
|
||||||
|
dst interface{}
|
||||||
|
expected interface{}
|
||||||
|
}{
|
||||||
|
{src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &s, expected: "foo"},
|
||||||
|
{src: pgtype.Text{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range simpleTests {
|
||||||
|
err := tt.src.AssignTo(tt.dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst := reflect.ValueOf(tt.dst).Elem().Interface(); dst != tt.expected {
|
||||||
|
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerAllocTests := []struct {
|
||||||
|
src pgtype.Text
|
||||||
|
dst interface{}
|
||||||
|
expected interface{}
|
||||||
|
}{
|
||||||
|
{src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &ps, expected: "foo"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range pointerAllocTests {
|
||||||
|
err := tt.src.AssignTo(tt.dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected {
|
||||||
|
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorTests := []struct {
|
||||||
|
src pgtype.Text
|
||||||
|
dst interface{}
|
||||||
|
}{
|
||||||
|
{src: pgtype.Text{Status: pgtype.Null}, dst: &s},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range errorTests {
|
||||||
|
err := tt.src.AssignTo(tt.dst)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,297 @@
|
||||||
|
package pgtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgio"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TextArray struct {
|
||||||
|
Elements []Text
|
||||||
|
Dimensions []ArrayDimension
|
||||||
|
Status Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *TextArray) ConvertFrom(src interface{}) error {
|
||||||
|
switch value := src.(type) {
|
||||||
|
case TextArray:
|
||||||
|
*dst = value
|
||||||
|
|
||||||
|
case []string:
|
||||||
|
if value == nil {
|
||||||
|
*dst = TextArray{Status: Null}
|
||||||
|
} else if len(value) == 0 {
|
||||||
|
*dst = TextArray{Status: Present}
|
||||||
|
} else {
|
||||||
|
elements := make([]Text, len(value))
|
||||||
|
for i := range value {
|
||||||
|
if err := elements[i].ConvertFrom(value[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = TextArray{
|
||||||
|
Elements: elements,
|
||||||
|
Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
|
||||||
|
Status: Present,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||||
|
return dst.ConvertFrom(originalSrc)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot convert %v to Text", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *TextArray) AssignTo(dst interface{}) error {
|
||||||
|
switch v := dst.(type) {
|
||||||
|
|
||||||
|
case *[]string:
|
||||||
|
if src.Status == Present {
|
||||||
|
*v = make([]string, len(src.Elements))
|
||||||
|
for i := range src.Elements {
|
||||||
|
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*v = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
|
return src.AssignTo(originalDst)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *TextArray) DecodeText(r io.Reader) error {
|
||||||
|
size, err := pgio.ReadInt32(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if size == -1 {
|
||||||
|
*dst = TextArray{Status: Null}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, int(size))
|
||||||
|
_, err = io.ReadFull(r, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
uta, err := ParseUntypedTextArray(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
textElementReader := NewTextElementReader(r)
|
||||||
|
var elements []Text
|
||||||
|
|
||||||
|
if len(uta.Elements) > 0 {
|
||||||
|
elements = make([]Text, len(uta.Elements))
|
||||||
|
|
||||||
|
for i, s := range uta.Elements {
|
||||||
|
var elem Text
|
||||||
|
textElementReader.Reset(s)
|
||||||
|
err = elem.DecodeText(textElementReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
elements[i] = elem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = TextArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *TextArray) DecodeBinary(r io.Reader) error {
|
||||||
|
size, err := pgio.ReadInt32(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if size == -1 {
|
||||||
|
*dst = TextArray{Status: Null}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayHeader ArrayHeader
|
||||||
|
err = arrayHeader.DecodeBinary(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(arrayHeader.Dimensions) == 0 {
|
||||||
|
*dst = TextArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
elementCount := arrayHeader.Dimensions[0].Length
|
||||||
|
for _, d := range arrayHeader.Dimensions[1:] {
|
||||||
|
elementCount *= d.Length
|
||||||
|
}
|
||||||
|
|
||||||
|
elements := make([]Text, elementCount)
|
||||||
|
|
||||||
|
for i := range elements {
|
||||||
|
err = elements[i].DecodeBinary(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst = TextArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *TextArray) EncodeText(w io.Writer) error {
|
||||||
|
if done, err := encodeNotPresent(w, src.Status); done {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src.Dimensions) == 0 {
|
||||||
|
_, err := pgio.WriteInt32(w, 2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write([]byte("{}"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
err := EncodeTextArrayDimensions(buf, src.Dimensions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dimElemCounts is the multiples of elements that each array lies on. For
|
||||||
|
// example, a single dimension array of length 4 would have a dimElemCounts of
|
||||||
|
// [4]. A multi-dimensional array of lengths [3,5,2] would have a
|
||||||
|
// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{'
|
||||||
|
// or '}'.
|
||||||
|
dimElemCounts := make([]int, len(src.Dimensions))
|
||||||
|
dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length)
|
||||||
|
for i := len(src.Dimensions) - 2; i > -1; i-- {
|
||||||
|
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, ',')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dec := range dimElemCounts {
|
||||||
|
if i%dec == 0 {
|
||||||
|
err = pgio.WriteByte(buf, '{')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textElementWriter.Reset()
|
||||||
|
if elem.String == "" && elem.Status == Present {
|
||||||
|
_, err := io.WriteString(buf, `""`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = elem.EncodeText(textElementWriter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dec := range dimElemCounts {
|
||||||
|
if (i+1)%dec == 0 {
|
||||||
|
err = pgio.WriteByte(buf, '}')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pgio.WriteInt32(w, int32(buf.Len()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.WriteTo(w)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *TextArray) EncodeBinary(w io.Writer) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
var arrayHeader ArrayHeader
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if src.Elements[i].Status == Null {
|
||||||
|
arrayHeader.ContainsNull = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package pgtype_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTextArrayTranscode(t *testing.T) {
|
||||||
|
testSuccessfulTranscode(t, "text[]", []interface{}{
|
||||||
|
&pgtype.TextArray{
|
||||||
|
Elements: nil,
|
||||||
|
Dimensions: nil,
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
&pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{
|
||||||
|
pgtype.Text{String: "foo", Status: pgtype.Present},
|
||||||
|
pgtype.Text{Status: pgtype.Null},
|
||||||
|
},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
&pgtype.TextArray{Status: pgtype.Null},
|
||||||
|
&pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{
|
||||||
|
pgtype.Text{String: "bar", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "baz", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "quz", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "", Status: pgtype.Present},
|
||||||
|
pgtype.Text{Status: pgtype.Null},
|
||||||
|
pgtype.Text{String: "foo", Status: pgtype.Present},
|
||||||
|
},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
&pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{
|
||||||
|
pgtype.Text{String: "bar", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "baz", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "quz", Status: pgtype.Present},
|
||||||
|
pgtype.Text{String: "foo", Status: pgtype.Present},
|
||||||
|
},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{
|
||||||
|
{Length: 2, LowerBound: 4},
|
||||||
|
{Length: 2, LowerBound: 2},
|
||||||
|
},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextArrayConvertFrom(t *testing.T) {
|
||||||
|
successfulTests := []struct {
|
||||||
|
source interface{}
|
||||||
|
result pgtype.TextArray
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
source: []string{"foo"},
|
||||||
|
result: pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||||
|
Status: pgtype.Present},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: (([]string)(nil)),
|
||||||
|
result: pgtype.TextArray{Status: pgtype.Null},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range successfulTests {
|
||||||
|
var r pgtype.TextArray
|
||||||
|
err := r.ConvertFrom(tt.source)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(r, tt.result) {
|
||||||
|
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextArrayAssignTo(t *testing.T) {
|
||||||
|
var stringSlice []string
|
||||||
|
type _stringSlice []string
|
||||||
|
var namedStringSlice _stringSlice
|
||||||
|
|
||||||
|
simpleTests := []struct {
|
||||||
|
src pgtype.TextArray
|
||||||
|
dst interface{}
|
||||||
|
expected interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
src: pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
dst: &stringSlice,
|
||||||
|
expected: []string{"foo"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{{String: "bar", Status: pgtype.Present}},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
dst: &namedStringSlice,
|
||||||
|
expected: _stringSlice{"bar"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: pgtype.TextArray{Status: pgtype.Null},
|
||||||
|
dst: &stringSlice,
|
||||||
|
expected: (([]string)(nil)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range simpleTests {
|
||||||
|
err := tt.src.AssignTo(tt.dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) {
|
||||||
|
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorTests := []struct {
|
||||||
|
src pgtype.TextArray
|
||||||
|
dst interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
src: pgtype.TextArray{
|
||||||
|
Elements: []pgtype.Text{{Status: pgtype.Null}},
|
||||||
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||||
|
Status: pgtype.Present,
|
||||||
|
},
|
||||||
|
dst: &stringSlice,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range errorTests {
|
||||||
|
err := tt.src.AssignTo(tt.dst)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,7 @@ func (src *TimestampArray) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (src *TimestamptzArray) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (src *<%= pgtype_array_type %>) AssignTo(dst interface{}) error {
|
||||||
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
if originalDst, ok := underlyingPtrSliceType(dst); ok {
|
||||||
return src.AssignTo(originalDst)
|
return src.AssignTo(originalDst)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("cannot put decode %v into %T", src, dst)
|
return fmt.Errorf("cannot decode %v into %T", src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -8,3 +8,4 @@ erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_type
|
||||||
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 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 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 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
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package pgtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VarcharArray TextArray
|
||||||
|
|
||||||
|
func (dst *VarcharArray) ConvertFrom(src interface{}) error {
|
||||||
|
return (*TextArray)(dst).ConvertFrom(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *VarcharArray) AssignTo(dst interface{}) error {
|
||||||
|
return (*TextArray)(src).AssignTo(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *VarcharArray) DecodeText(r io.Reader) error {
|
||||||
|
return (*TextArray)(dst).DecodeText(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dst *VarcharArray) DecodeBinary(r io.Reader) error {
|
||||||
|
return (*TextArray)(dst).DecodeBinary(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *VarcharArray) EncodeText(w io.Writer) error {
|
||||||
|
return (*TextArray)(src).EncodeText(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (src *VarcharArray) EncodeBinary(w io.Writer) error {
|
||||||
|
return (*TextArray)(src).encodeBinary(w, VarcharOID)
|
||||||
|
}
|
32
query.go
32
query.go
|
@ -388,22 +388,6 @@ func (rows *Rows) Values() ([]interface{}, error) {
|
||||||
values = append(values, decodeFloat4(vr))
|
values = append(values, decodeFloat4(vr))
|
||||||
case Float8OID:
|
case Float8OID:
|
||||||
values = append(values, decodeFloat8(vr))
|
values = append(values, decodeFloat8(vr))
|
||||||
case BoolArrayOID:
|
|
||||||
values = append(values, decodeBoolArray(vr))
|
|
||||||
case Int2ArrayOID:
|
|
||||||
values = append(values, decodeInt2Array(vr))
|
|
||||||
case Int4ArrayOID:
|
|
||||||
values = append(values, decodeInt4Array(vr))
|
|
||||||
case Int8ArrayOID:
|
|
||||||
values = append(values, decodeInt8Array(vr))
|
|
||||||
case Float4ArrayOID:
|
|
||||||
values = append(values, decodeFloat4Array(vr))
|
|
||||||
case Float8ArrayOID:
|
|
||||||
values = append(values, decodeFloat8Array(vr))
|
|
||||||
case TextArrayOID, VarcharArrayOID:
|
|
||||||
values = append(values, decodeTextArray(vr))
|
|
||||||
case TimestampArrayOID, TimestampTzArrayOID:
|
|
||||||
values = append(values, decodeTimestampArray(vr))
|
|
||||||
case DateOID:
|
case DateOID:
|
||||||
values = append(values, decodeDate(vr))
|
values = append(values, decodeDate(vr))
|
||||||
case TimestampTzOID:
|
case TimestampTzOID:
|
||||||
|
@ -479,22 +463,6 @@ func (rows *Rows) ValuesForStdlib() ([]interface{}, error) {
|
||||||
values = append(values, decodeFloat4(vr))
|
values = append(values, decodeFloat4(vr))
|
||||||
case Float8OID:
|
case Float8OID:
|
||||||
values = append(values, decodeFloat8(vr))
|
values = append(values, decodeFloat8(vr))
|
||||||
case BoolArrayOID:
|
|
||||||
values = append(values, decodeBoolArray(vr))
|
|
||||||
case Int2ArrayOID:
|
|
||||||
values = append(values, decodeInt2Array(vr))
|
|
||||||
case Int4ArrayOID:
|
|
||||||
values = append(values, decodeInt4Array(vr))
|
|
||||||
case Int8ArrayOID:
|
|
||||||
values = append(values, decodeInt8Array(vr))
|
|
||||||
case Float4ArrayOID:
|
|
||||||
values = append(values, decodeFloat4Array(vr))
|
|
||||||
case Float8ArrayOID:
|
|
||||||
values = append(values, decodeFloat8Array(vr))
|
|
||||||
case TextArrayOID, VarcharArrayOID:
|
|
||||||
values = append(values, decodeTextArray(vr))
|
|
||||||
case TimestampArrayOID, TimestampTzArrayOID:
|
|
||||||
values = append(values, decodeTimestampArray(vr))
|
|
||||||
case DateOID:
|
case DateOID:
|
||||||
values = append(values, decodeDate(vr))
|
values = append(values, decodeDate(vr))
|
||||||
case TimestampTzOID:
|
case TimestampTzOID:
|
||||||
|
|
|
@ -1179,9 +1179,6 @@ func TestQueryRowCoreStringSlice(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected null to cause error when scanned into slice, but it didn't")
|
t.Error("Expected null to cause error when scanned into slice, but it didn't")
|
||||||
}
|
}
|
||||||
if err != nil && !strings.Contains(err.Error(), "Cannot decode null") {
|
|
||||||
t.Errorf(`Expected null to cause error "Cannot decode null..." but it was %v`, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureConnValid(t, conn)
|
ensureConnValid(t, conn)
|
||||||
}
|
}
|
||||||
|
|
72
values.go
72
values.go
|
@ -1073,8 +1073,6 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := arg.(type) {
|
switch arg := arg.(type) {
|
||||||
case []string:
|
|
||||||
return encodeStringSlice(wbuf, oid, arg)
|
|
||||||
case Char:
|
case Char:
|
||||||
return encodeChar(wbuf, oid, arg)
|
return encodeChar(wbuf, oid, arg)
|
||||||
case AclItem:
|
case AclItem:
|
||||||
|
@ -1178,8 +1176,6 @@ func Decode(vr *ValueReader, d interface{}) error {
|
||||||
*v = decodeText(vr)
|
*v = decodeText(vr)
|
||||||
case *[]AclItem:
|
case *[]AclItem:
|
||||||
*v = decodeAclItemArray(vr)
|
*v = decodeAclItemArray(vr)
|
||||||
case *[]string:
|
|
||||||
*v = decodeTextArray(vr)
|
|
||||||
case *[][]byte:
|
case *[][]byte:
|
||||||
*v = decodeByteaArray(vr)
|
*v = decodeByteaArray(vr)
|
||||||
case *[]interface{}:
|
case *[]interface{}:
|
||||||
|
@ -2569,41 +2565,6 @@ func encodeFloat64Slice(w *WriteBuf, oid OID, slice []float64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeTextArray(vr *ValueReader) []string {
|
|
||||||
if vr.Len() == -1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vr.Type().DataType != TextArrayOID && vr.Type().DataType != VarcharArrayOID {
|
|
||||||
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode oid %v into []string", vr.Type().DataType)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vr.Type().FormatCode != BinaryFormatCode {
|
|
||||||
vr.Fatal(ProtocolError(fmt.Sprintf("Unknown field description format code: %v", vr.Type().FormatCode)))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
numElems, err := decode1dArrayHeader(vr)
|
|
||||||
if err != nil {
|
|
||||||
vr.Fatal(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
a := make([]string, int(numElems))
|
|
||||||
for i := 0; i < len(a); i++ {
|
|
||||||
elSize := vr.ReadInt32()
|
|
||||||
if elSize == -1 {
|
|
||||||
vr.Fatal(ProtocolError("Cannot decode null element"))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
a[i] = vr.ReadString(elSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeAclItem escapes an AclItem before it is added to
|
// escapeAclItem escapes an AclItem before it is added to
|
||||||
// its aclitem[] string representation. The PostgreSQL aclitem
|
// its aclitem[] string representation. The PostgreSQL aclitem
|
||||||
// datatype itself can need escapes because it follows the
|
// datatype itself can need escapes because it follows the
|
||||||
|
@ -2808,39 +2769,6 @@ func decodeAclItemArray(vr *ValueReader) []AclItem {
|
||||||
return aclItems
|
return aclItems
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeStringSlice(w *WriteBuf, oid OID, slice []string) error {
|
|
||||||
var elOID OID
|
|
||||||
switch oid {
|
|
||||||
case VarcharArrayOID:
|
|
||||||
elOID = VarcharOID
|
|
||||||
case TextArrayOID:
|
|
||||||
elOID = TextOID
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cannot encode Go %s into oid %d", "[]string", oid)
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalStringSize int
|
|
||||||
for _, v := range slice {
|
|
||||||
totalStringSize += len(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
size := 20 + len(slice)*4 + totalStringSize
|
|
||||||
w.WriteInt32(int32(size))
|
|
||||||
|
|
||||||
w.WriteInt32(1) // number of dimensions
|
|
||||||
w.WriteInt32(0) // no nulls
|
|
||||||
w.WriteInt32(int32(elOID)) // type of elements
|
|
||||||
w.WriteInt32(int32(len(slice))) // number of elements
|
|
||||||
w.WriteInt32(1) // index of first element
|
|
||||||
|
|
||||||
for _, v := range slice {
|
|
||||||
w.WriteInt32(int32(len(v)))
|
|
||||||
w.WriteBytes([]byte(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeTimestampArray(vr *ValueReader) []time.Time {
|
func decodeTimestampArray(vr *ValueReader) []time.Time {
|
||||||
if vr.Len() == -1 {
|
if vr.Len() == -1 {
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue