diff --git a/pgtype/array.go b/pgtype/array.go index 0fa4c129b..7dfee389e 100644 --- a/pgtype/array.go +++ b/pgtype/array.go @@ -363,12 +363,13 @@ func quoteArrayElement(src string) string { } func isSpace(ch byte) bool { - // see https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/parser/scansup.c#L224 - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\f' + // see array_isspace: + // 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 { - 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 src diff --git a/pgtype/array_codec_test.go b/pgtype/array_codec_test.go index 86d03b574..0bb83aa03 100644 --- a/pgtype/array_codec_test.go +++ b/pgtype/array_codec_test.go @@ -3,6 +3,7 @@ package pgtype_test import ( "context" "encoding/hex" + "reflect" "strings" "testing" @@ -51,23 +52,35 @@ func TestArrayCodec(t *testing.T) { }) } -func TestArrayCodecFlatArray(t *testing.T) { +func TestArrayCodecFlatArrayString(t *testing.T) { + testCases := []struct { + input []string + }{ + {nil}, + {[]string{}}, + {[]string{"a"}}, + {[]string{"a", "b"}}, + // previously had a bug with whitespace handling + {[]string{"\v", "\t", "\n", "\r", "\f", " "}}, + {[]string{"a\vb", "a\tb", "a\nb", "a\rb", "a\fb", "a b"}}, + } + + queryModes := []pgx.QueryExecMode{pgx.QueryExecModeSimpleProtocol, pgx.QueryExecModeDescribeExec} + defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { - for i, tt := range []struct { - expected any - }{ - {pgtype.FlatArray[int32](nil)}, - {pgtype.FlatArray[int32]{}}, - {pgtype.FlatArray[int32]{1, 2, 3}}, - } { - var actual pgtype.FlatArray[int32] - err := conn.QueryRow( - ctx, - "select $1::int[]", - tt.expected, - ).Scan(&actual) - assert.NoErrorf(t, err, "%d", i) - assert.Equalf(t, tt.expected, actual, "%d", i) + 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) + } + } } }) }