mirror of https://github.com/jackc/pgx.git
pgtype array: Fix encoding of vtab \v
Arrays with values that start or end with vtab ("\v") must be quoted. Postgres's array parser skips leading and trailing whitespace with the array_isspace() function, which is slightly different from the scanner_isspace() function that was previously linked. Add a test that reproduces this failure, and fix the definition of isSpace. This also includes a change to use strings.EqualFold which should really not matter, but does not require copying the string.pull/1651/head
parent
5b7cc8e215
commit
e5db6a0467
|
@ -363,12 +363,13 @@ func quoteArrayElement(src string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSpace(ch byte) bool {
|
func isSpace(ch byte) bool {
|
||||||
// see https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/parser/scansup.c#L224
|
// see array_isspace:
|
||||||
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f'
|
// https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/arrayfuncs.c
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || 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.EqualFold(src, "null")) || isSpace(src[0]) || isSpace(src[len(src)-1]) || strings.ContainsAny(src, `{},"\`) {
|
||||||
return quoteArrayElement(src)
|
return quoteArrayElement(src)
|
||||||
}
|
}
|
||||||
return src
|
return src
|
||||||
|
|
|
@ -3,6 +3,7 @@ package pgtype_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -51,23 +52,35 @@ func TestArrayCodec(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestArrayCodecFlatArray(t *testing.T) {
|
func TestArrayCodecFlatArrayString(t *testing.T) {
|
||||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
testCases := []struct {
|
||||||
for i, tt := range []struct {
|
input []string
|
||||||
expected any
|
|
||||||
}{
|
}{
|
||||||
{pgtype.FlatArray[int32](nil)},
|
{nil},
|
||||||
{pgtype.FlatArray[int32]{}},
|
{[]string{}},
|
||||||
{pgtype.FlatArray[int32]{1, 2, 3}},
|
{[]string{"a"}},
|
||||||
} {
|
{[]string{"a", "b"}},
|
||||||
var actual pgtype.FlatArray[int32]
|
// previously had a bug with whitespace handling
|
||||||
err := conn.QueryRow(
|
{[]string{"\v", "\t", "\n", "\r", "\f", " "}},
|
||||||
ctx,
|
{[]string{"a\vb", "a\tb", "a\nb", "a\rb", "a\fb", "a b"}},
|
||||||
"select $1::int[]",
|
}
|
||||||
tt.expected,
|
|
||||||
).Scan(&actual)
|
queryModes := []pgx.QueryExecMode{pgx.QueryExecModeSimpleProtocol, pgx.QueryExecModeDescribeExec}
|
||||||
assert.NoErrorf(t, err, "%d", i)
|
|
||||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
for _, queryMode := range queryModes {
|
||||||
|
var out []string
|
||||||
|
err := conn.QueryRow(ctx, "select $1::text[]", queryMode, testCase.input).Scan(&out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("i=%d input=%#v queryMode=%s: Scan failed: %s",
|
||||||
|
i, testCase.input, queryMode, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, testCase.input) {
|
||||||
|
t.Errorf("i=%d input=%#v queryMode=%s: not equal output=%#v",
|
||||||
|
i, testCase.input, queryMode, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue