mirror of
https://github.com/jackc/pgx.git
synced 2025-05-31 11:42:24 +00:00
Make array helpers private
This commit is contained in:
parent
1c90746cf5
commit
cc7de81d3b
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user