From 3e2b9ef2b6b43fb06efa2ad7ab42466e2731096e Mon Sep 17 00:00:00 2001 From: pgaddict Date: Thu, 14 Sep 2023 15:47:52 +0800 Subject: [PATCH v9 7/7] refactor ReadArrayStr. array ending errors(unnessary junk) can happen in many cases, like inside double or not, with backslash or not. so conslidate it, using a goto ending_errror. add more tests, also cover doule quote cases. expect_delim state is not enought for case like '{{},}'. You need tracking current token and previous token to solve cases where a delimiter followed by a closing brace. --- src/backend/utils/adt/arrayfuncs.c | 73 ++++++++++++---------------- src/test/regress/expected/arrays.out | 38 +++++++++++++++ src/test/regress/sql/arrays.sql | 9 ++++ 3 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 95a3d5f6..ab685caa 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -299,20 +299,25 @@ array_in(PG_FUNCTION_ARGS) errdetail("Junk after closing right brace."))); } +/* ---------- + * Uncomment the following to get a dump of a array's ndim, dim, lBound. + * ---------- +#define ARRAYDEBUG + */ #ifdef ARRAYDEBUG { StringInfoData buf; initStringInfo(&buf); - appendStringInfo(&buf, "array_in- ndim %d (", ndim); + appendStringInfo(&buf, "array_in- ndim %d, dim info(", ndim); for (int i = 0; i < MAXDIM; i++) appendStringInfo(&buf, " %d", dim[i]); - appendStringInfo(&buf, "lBound info"); + appendStringInfo(&buf, "); lBound info("); for (int i = 0; i < MAXDIM; i++) appendStringInfo(&buf, " %d", lBound[i]); - appendStringInfo(&buf, ") for %s\n", string); - elog(NOTICE, "%s", buf.data); + appendStringInfo(&buf, ") for %s", string); + elog(DEBUG1, "%s", buf.data); pfree(buf.data); } #endif @@ -596,7 +601,7 @@ ReadArrayStr(char **srcptr, bool dimensions_specified = ndim != 0; bool ndim_frozen; int nelems[MAXDIM]; - + ArrayToken prev_tok; /* The caller already checked this */ Assert(**srcptr == '{'); @@ -622,6 +627,7 @@ ReadArrayStr(char **srcptr, expect_delim = false; nest_level = 0; nitems = 0; + prev_tok = ATOK_LEVEL_START; /* initialize it */ do { ArrayToken tok; @@ -655,7 +661,8 @@ ReadArrayStr(char **srcptr, } break; case ATOK_LEVEL_END: - if (nest_level == 0) + /* cannot precede with a delim. nest_level should larger than 0 */ + if (prev_tok == ATOK_DELIM || nest_level == 0) ereturn(escontext, false, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed array literal: \"%s\"", origStr), @@ -739,6 +746,7 @@ ReadArrayStr(char **srcptr, case ATOK_ERROR: return false; } + prev_tok = tok; } while (nest_level > 0); pfree(elembuf); @@ -788,12 +796,7 @@ ReadArrayToken(char **srcptr, char *elembuf, char typdelim, switch (*p) { case '\0': - errsave(escontext, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); - *srcptr = p; - return ATOK_ERROR; + goto ending_error; case '{': *srcptr = p + 1; return ATOK_LEVEL_START; @@ -825,24 +828,12 @@ quoted_element: switch (*p) { case '\0': - errsave(escontext, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); - *srcptr = p; - return ATOK_ERROR; + goto ending_error; case '\\': /* Skip backslash, copy next character as-is. */ p++; if (*p == '\0') - { - errsave(escontext, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); - *srcptr = p; - return ATOK_ERROR; - } + goto ending_error; *dst++ = *p++; break; case '"': @@ -863,31 +854,20 @@ unquoted_element: { case '\0': case '{': - errsave(escontext, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); - *srcptr = p; - return ATOK_ERROR; + goto ending_error; case '\\': /* Skip backslash, copy next character as-is. */ p++; if (*p == '\0') - { - errsave(escontext, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); - *srcptr = p; - return ATOK_ERROR; - } + goto ending_error; *dst++ = *p++; + dstendptr = dst; break; case '"': errsave(escontext, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("malformed array literal: \"%s\"", - origStr))); + errmsg("malformed array literal: \"%s\"",origStr), + errdetail("Unexpected array element."))); *srcptr = p; return ATOK_ERROR; @@ -907,6 +887,15 @@ unquoted_element: p++; } } + +ending_error: + errsave(escontext, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("malformed array literal: \"%s\"", + origStr), + errdetail("Unexpected end of input."))); + *srcptr = p; + return ATOK_ERROR; } /* diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index bea0d00b..0d9fef55 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -1481,6 +1481,16 @@ ERROR: malformed array literal: "{{1,2},\{2,3}}" LINE 1: select E'{{1,2},\\{2,3}}'::text[]; ^ DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions. +select E'{"a"a}'::text[]; +ERROR: malformed array literal: "{"a"a}" +LINE 1: select E'{"a"a}'::text[]; + ^ +DETAIL: Unexpected array element. +select E'{a"a"}'::text[]; +ERROR: malformed array literal: "{a"a"}" +LINE 1: select E'{a"a"}'::text[]; + ^ +DETAIL: Unexpected array element. select '{{"1 2" x},{3}}'::text[]; ERROR: malformed array literal: "{{"1 2" x},{3}}" LINE 1: select '{{"1 2" x},{3}}'::text[]; @@ -1544,6 +1554,34 @@ ERROR: cannot determine type of empty array LINE 1: select array[]; ^ HINT: Explicitly cast to the desired type, for example ARRAY[]::integer[]. +select '{{1,},{1},}'::text[]; +ERROR: malformed array literal: "{{1,},{1},}" +LINE 1: select '{{1,},{1},}'::text[]; + ^ +DETAIL: Unexpected "}" character. +select '{{1,},{1}}'::text[]; +ERROR: malformed array literal: "{{1,},{1}}" +LINE 1: select '{{1,},{1}}'::text[]; + ^ +DETAIL: Unexpected "}" character. +select '{{1,}}'::text[]; +ERROR: malformed array literal: "{{1,}}" +LINE 1: select '{{1,}}'::text[]; + ^ +DETAIL: Unexpected "}" character. +select '{1,}'::text[]; +ERROR: malformed array literal: "{1,}" +LINE 1: select '{1,}'::text[]; + ^ +DETAIL: Unexpected "}" character. +select '[21474836488:21474836489]={1,2}'::int[]; +ERROR: array bound is out of range +LINE 1: select '[21474836488:21474836489]={1,2}'::int[]; + ^ +select '[-2147483649:-2147483648]={1,2}'::int[]; +ERROR: array bound is out of range +LINE 1: select '[-2147483649:-2147483648]={1,2}'::int[]; + ^ -- none of the above should be accepted -- all of the following should be accepted select '{}'::text[]; diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 5eacb351..47667ea3 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -455,6 +455,8 @@ select 'foo' ilike all (array['F%', '%O']); -- t -- none of the following should be accepted select '{{1,{2}},{2,3}}'::text[]; select E'{{1,2},\\{2,3}}'::text[]; +select E'{"a"a}'::text[]; +select E'{a"a"}'::text[]; select '{{"1 2" x},{3}}'::text[]; select '{}}'::text[]; select '{ }}'::text[]; @@ -468,6 +470,13 @@ select '[2147483646:2147483647]={1,2}'::int[]; select '[1:-1]={}'::int[]; select '[1:0]={1}'::int[]; select array[]; +select '{{1,},{1},}'::text[]; +select '{{1,},{1}}'::text[]; +select '{{1,}}'::text[]; +select '{1,}'::text[]; +select '[21474836488:21474836489]={1,2}'::int[]; +select '[-2147483649:-2147483648]={1,2}'::int[]; + -- none of the above should be accepted -- all of the following should be accepted -- 2.34.1