Make array helpers private

This commit is contained in:
Jack Christensen 2022-04-16 14:21:40 -05:00
parent 1c90746cf5
commit cc7de81d3b
5 changed files with 40 additions and 41 deletions

View File

@ -97,6 +97,7 @@ This matches the convention set by `database/sql`. In addition, for comparable t
* Renamed `pgtype.DataType` to `pgtype.Type`. * Renamed `pgtype.DataType` to `pgtype.Type`.
* Renamed `pgtype.None` to `pgtype.Finite`. * Renamed `pgtype.None` to `pgtype.Finite`.
* `RegisterType` now accepts a `*Type` instead of `Type`. * `RegisterType` now accepts a `*Type` instead of `Type`.
* Assorted array helper methods and types made private.
## stdlib ## stdlib

View File

@ -17,7 +17,7 @@ import (
// src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of // src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of
// particular interest is the array_send function. // particular interest is the array_send function.
type ArrayHeader struct { type arrayHeader struct {
ContainsNull bool ContainsNull bool
ElementOID uint32 ElementOID uint32
Dimensions []ArrayDimension Dimensions []ArrayDimension
@ -42,7 +42,7 @@ func cardinality(dimensions []ArrayDimension) int {
return elementCount return elementCount
} }
func (dst *ArrayHeader) DecodeBinary(m *Map, src []byte) (int, error) { func (dst *arrayHeader) DecodeBinary(m *Map, src []byte) (int, error) {
if len(src) < 12 { if len(src) < 12 {
return 0, fmt.Errorf("array header too short: %d", len(src)) return 0, fmt.Errorf("array header too short: %d", len(src))
} }
@ -73,7 +73,7 @@ func (dst *ArrayHeader) DecodeBinary(m *Map, src []byte) (int, error) {
return rp, nil return rp, nil
} }
func (src ArrayHeader) EncodeBinary(buf []byte) []byte { func (src arrayHeader) EncodeBinary(buf []byte) []byte {
buf = pgio.AppendInt32(buf, int32(len(src.Dimensions))) buf = pgio.AppendInt32(buf, int32(len(src.Dimensions)))
var containsNull int32 var containsNull int32
@ -92,14 +92,14 @@ func (src ArrayHeader) EncodeBinary(buf []byte) []byte {
return buf return buf
} }
type UntypedTextArray struct { type untypedTextArray struct {
Elements []string Elements []string
Quoted []bool Quoted []bool
Dimensions []ArrayDimension Dimensions []ArrayDimension
} }
func ParseUntypedTextArray(src string) (*UntypedTextArray, error) { func parseUntypedTextArray(src string) (*untypedTextArray, error) {
dst := &UntypedTextArray{ dst := &untypedTextArray{
Elements: []string{}, Elements: []string{},
Quoted: []bool{}, Quoted: []bool{},
Dimensions: []ArrayDimension{}, Dimensions: []ArrayDimension{},
@ -333,7 +333,7 @@ func arrayParseInteger(buf *bytes.Buffer) (int32, error) {
} }
} }
func EncodeTextArrayDimensions(buf []byte, dimensions []ArrayDimension) []byte { func encodeTextArrayDimensions(buf []byte, dimensions []ArrayDimension) []byte {
var customDimensions bool var customDimensions bool
for _, dim := range dimensions { for _, dim := range dimensions {
if dim.LowerBound != 1 { if dim.LowerBound != 1 {
@ -367,7 +367,7 @@ func isSpace(ch byte) bool {
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f' return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f'
} }
func QuoteArrayElementIfNeeded(src string) string { func quoteArrayElementIfNeeded(src string) string {
if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || isSpace(src[0]) || isSpace(src[len(src)-1]) || strings.ContainsAny(src, `{},"\`) { if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || isSpace(src[0]) || isSpace(src[len(src)-1]) || strings.ContainsAny(src, `{},"\`) {
return quoteArrayElement(src) return quoteArrayElement(src)
} }

View File

@ -91,7 +91,7 @@ func (p *encodePlanArrayCodecText) Encode(value any, buf []byte) (newBuf []byte,
return append(buf, '{', '}'), nil return append(buf, '{', '}'), nil
} }
buf = EncodeTextArrayDimensions(buf, dimensions) buf = encodeTextArrayDimensions(buf, dimensions)
// dimElemCounts is the multiples of elements that each array lies on. For // 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 // example, a single dimension array of length 4 would have a dimElemCounts of
@ -138,7 +138,7 @@ func (p *encodePlanArrayCodecText) Encode(value any, buf []byte) (newBuf []byte,
if elemBuf == nil { if elemBuf == nil {
buf = append(buf, `NULL`...) buf = append(buf, `NULL`...)
} else { } else {
buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) buf = append(buf, quoteArrayElementIfNeeded(string(elemBuf))...)
} }
for _, dec := range dimElemCounts { for _, dec := range dimElemCounts {
@ -165,7 +165,7 @@ func (p *encodePlanArrayCodecBinary) Encode(value any, buf []byte) (newBuf []byt
return nil, nil return nil, nil
} }
arrayHeader := ArrayHeader{ arrayHeader := arrayHeader{
Dimensions: dimensions, Dimensions: dimensions,
ElementOID: p.ac.ElementType.OID, ElementOID: p.ac.ElementType.OID,
} }
@ -232,7 +232,7 @@ func (c *ArrayCodec) PlanScan(m *Map, oid uint32, format int16, target any) Scan
} }
func (c *ArrayCodec) decodeBinary(m *Map, arrayOID uint32, src []byte, array ArraySetter) error { func (c *ArrayCodec) decodeBinary(m *Map, arrayOID uint32, src []byte, array ArraySetter) error {
var arrayHeader ArrayHeader var arrayHeader arrayHeader
rp, err := arrayHeader.DecodeBinary(m, src) rp, err := arrayHeader.DecodeBinary(m, src)
if err != nil { if err != nil {
return err return err
@ -272,7 +272,7 @@ func (c *ArrayCodec) decodeBinary(m *Map, arrayOID uint32, src []byte, array Arr
} }
func (c *ArrayCodec) decodeText(m *Map, arrayOID uint32, src []byte, array ArraySetter) error { func (c *ArrayCodec) decodeText(m *Map, arrayOID uint32, src []byte, array ArraySetter) error {
uta, err := ParseUntypedTextArray(string(src)) uta, err := parseUntypedTextArray(string(src))
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,79 +1,77 @@
package pgtype_test package pgtype
import ( import (
"reflect" "reflect"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype"
) )
func TestParseUntypedTextArray(t *testing.T) { func TestParseUntypedTextArray(t *testing.T) {
tests := []struct { tests := []struct {
source string source string
result pgtype.UntypedTextArray result untypedTextArray
}{ }{
{ {
source: "{}", source: "{}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{}, Elements: []string{},
Quoted: []bool{}, Quoted: []bool{},
Dimensions: []pgtype.ArrayDimension{}, Dimensions: []ArrayDimension{},
}, },
}, },
{ {
source: "{1}", source: "{1}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"1"}, Elements: []string{"1"},
Quoted: []bool{false}, Quoted: []bool{false},
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},
}, },
}, },
{ {
source: "{a,b}", source: "{a,b}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"a", "b"}, Elements: []string{"a", "b"},
Quoted: []bool{false, false}, Quoted: []bool{false, false},
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 2, LowerBound: 1}},
}, },
}, },
{ {
source: `{"NULL"}`, source: `{"NULL"}`,
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"NULL"}, Elements: []string{"NULL"},
Quoted: []bool{true}, Quoted: []bool{true},
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},
}, },
}, },
{ {
source: `{""}`, source: `{""}`,
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{""}, Elements: []string{""},
Quoted: []bool{true}, Quoted: []bool{true},
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},
}, },
}, },
{ {
source: `{"He said, \"Hello.\""}`, source: `{"He said, \"Hello.\""}`,
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{`He said, "Hello."`}, Elements: []string{`He said, "Hello."`},
Quoted: []bool{true}, Quoted: []bool{true},
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 1, LowerBound: 1}},
}, },
}, },
{ {
source: "{{a,b},{c,d},{e,f}}", source: "{{a,b},{c,d},{e,f}}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"a", "b", "c", "d", "e", "f"}, Elements: []string{"a", "b", "c", "d", "e", "f"},
Quoted: []bool{false, false, false, false, false, false}, Quoted: []bool{false, false, false, false, false, false},
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, Dimensions: []ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
}, },
}, },
{ {
source: "{{{a,b},{c,d},{e,f}},{{a,b},{c,d},{e,f}}}", source: "{{{a,b},{c,d},{e,f}},{{a,b},{c,d},{e,f}}}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"a", "b", "c", "d", "e", "f", "a", "b", "c", "d", "e", "f"}, Elements: []string{"a", "b", "c", "d", "e", "f", "a", "b", "c", "d", "e", "f"},
Quoted: []bool{false, false, false, false, false, false, false, false, false, false, false, false}, Quoted: []bool{false, false, false, false, false, false, false, false, false, false, false, false},
Dimensions: []pgtype.ArrayDimension{ Dimensions: []ArrayDimension{
{Length: 2, LowerBound: 1}, {Length: 2, LowerBound: 1},
{Length: 3, LowerBound: 1}, {Length: 3, LowerBound: 1},
{Length: 2, LowerBound: 1}, {Length: 2, LowerBound: 1},
@ -82,18 +80,18 @@ func TestParseUntypedTextArray(t *testing.T) {
}, },
{ {
source: "[4:4]={1}", source: "[4:4]={1}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"1"}, Elements: []string{"1"},
Quoted: []bool{false}, Quoted: []bool{false},
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 4}}, Dimensions: []ArrayDimension{{Length: 1, LowerBound: 4}},
}, },
}, },
{ {
source: "[4:5][2:3]={{a,b},{c,d}}", source: "[4:5][2:3]={{a,b},{c,d}}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"a", "b", "c", "d"}, Elements: []string{"a", "b", "c", "d"},
Quoted: []bool{false, false, false, false}, Quoted: []bool{false, false, false, false},
Dimensions: []pgtype.ArrayDimension{ Dimensions: []ArrayDimension{
{Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 4},
{Length: 2, LowerBound: 2}, {Length: 2, LowerBound: 2},
}, },
@ -101,16 +99,16 @@ func TestParseUntypedTextArray(t *testing.T) {
}, },
{ {
source: "[-4:-2]={1,2,3}", source: "[-4:-2]={1,2,3}",
result: pgtype.UntypedTextArray{ result: untypedTextArray{
Elements: []string{"1", "2", "3"}, Elements: []string{"1", "2", "3"},
Quoted: []bool{false, false, false}, Quoted: []bool{false, false, false},
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: -4}}, Dimensions: []ArrayDimension{{Length: 3, LowerBound: -4}},
}, },
}, },
} }
for i, tt := range tests { for i, tt := range tests {
r, err := pgtype.ParseUntypedTextArray(tt.source) r, err := parseUntypedTextArray(tt.source)
if err != nil { if err != nil {
t.Errorf("%d: %v", i, err) t.Errorf("%d: %v", i, err)
continue continue

View File

@ -13,7 +13,7 @@ pgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL
Array Support Array Support
ArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an ArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an
ArrayCodec for the appropriate PostgreSQL OID. ArrayCodec for the appropriate PostgreSQL OID. In addition, Array[T] type can support multi-dimensional arrays.
Composite Support Composite Support