v10-0007-refactor-ReadArrayStr.patch

text/x-patch

Filename: v10-0007-refactor-ReadArrayStr.patch
Type: text/x-patch
Part: 6
Message: Re: Cleaning up array_in()

Patch

Format: format-patch
Series: patch v10-0007
Subject: refactor ReadArrayStr.
File+
src/backend/utils/adt/arrayfuncs.c 31 42
src/test/regress/expected/arrays.out 38 0
src/test/regress/sql/arrays.sql 9 0
From 3e2b9ef2b6b43fb06efa2ad7ab42466e2731096e Mon Sep 17 00:00:00 2001
From: pgaddict <jian.universality@gmail.com>
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