hi.
based on Heikki v3.
I made some changes:
array_in: dim[6] all initialize with -1, lBound[6] all initialize with 1.
if ReadArrayDimensions called, then corresponding dimension lBound
will replace the initialized default 1 value.
ReadArrayStr, since array_in main function initialized dim array,
dimensions_specified true or false, I don't need to initialize again,
so I deleted that part.

to solve corner cases like  '{{1,},{1},}'::text[]. in ReadArrayStr
main switch function, like other ArrayToken, first evaluate
expect_delim then assign expect_delim.
In ATOK_LEVEL_END. if non-empty array, closing bracket either precede
with an element or another closing element. In both cases, the
previous expect_delim should be true.

in
         * FIXME: Is this still required? I believe all the checks it
performs are
         * redundant with other checks in ReadArrayDimension() and
ReadArrayStr()
         */
I deleted
-       nitems_according_to_dims = ArrayGetNItemsSafe(ndim, dim, escontext);
-       if (nitems_according_to_dims < 0)
-               PG_RETURN_NULL();
-       if (nitems != nitems_according_to_dims)
-               elog(ERROR, "mismatch nitems, %d vs %d", nitems,
nitems_according_to_dims);
but I am not sure if the following is necessary.
      if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext))
                PG_RETURN_NULL();

I added some corner case tests like select '{{1,},{1},}'::text[];

some changes broken:
select '{{1},{}}'::text[];
-DETAIL:  Multidimensional arrays must have sub-arrays with matching dimensions.
+DETAIL:  Unexpected "," character.
I added some error checks in ATOK_LEVEL_END. The first expect_delim
part check will first generate an error, the dimension error part will
not be reached.
From e59ccd4fa6dda94c50d5b0a2f9388dd8d859d501 Mon Sep 17 00:00:00 2001
From: pgaddict <jian.universal...@gmail.com>
Date: Mon, 7 Aug 2023 12:49:57 +0800
Subject: [PATCH v4] based on Heikki verion3

---
 src/backend/utils/adt/arrayfuncs.c   | 45 ++++++++++++++++++----------
 src/test/regress/expected/arrays.out | 22 +++++++++++++-
 src/test/regress/sql/arrays.sql      |  4 +++
 3 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 19d7af6d..03b6b56d 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -233,6 +233,11 @@ array_in(PG_FUNCTION_ARGS)
 	typdelim = my_extra->typdelim;
 	typioparam = my_extra->typioparam;
 
+	for (int i = 0; i < MAXDIM; i++)
+	{
+		dim[i] = -1;
+		lBound[i] = 1;
+	}
 	/*
 	 * Start processing the input string.
 	 *
@@ -245,14 +250,12 @@ array_in(PG_FUNCTION_ARGS)
 
 	if (ndim == 0)
 	{
-		/* No array dimensions, so intuit dimensions from brace structure */
+		/* No array dimensions, so init dimensions from brace structure */
 		if (*p != '{')
 			ereturn(escontext, (Datum) 0,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("malformed array literal: \"%s\"", string),
 					 errdetail("Array value must start with \"{\" or dimension information.")));
-		for (int i = 0; i < MAXDIM; i++)
-			lBound[i] = 1;
 	}
 	else
 	{
@@ -304,8 +307,11 @@ array_in(PG_FUNCTION_ARGS)
 		initStringInfo(&buf);
 
 		appendStringInfo(&buf, "array_in- ndim %d (", ndim);
-		for (int i = 0; i < ndim; i++)
+		for (int i = 0; i < MAXDIM; i++)
 			appendStringInfo(&buf, " %d", dim[i]);
+		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);
 		pfree(buf.data);
@@ -318,11 +324,6 @@ array_in(PG_FUNCTION_ARGS)
 	 * FIXME: Is this still required? I believe all the checks it performs are
 	 * redundant with other checks in ReadArrayDimension() and ReadArrayStr()
 	 */
-	nitems_according_to_dims = ArrayGetNItemsSafe(ndim, dim, escontext);
-	if (nitems_according_to_dims < 0)
-		PG_RETURN_NULL();
-	if (nitems != nitems_according_to_dims)
-		elog(ERROR, "mismatch nitems, %d vs %d", nitems, nitems_according_to_dims);
 	if (!ArrayCheckBoundsSafe(ndim, dim, lBound, escontext))
 		PG_RETURN_NULL();
 
@@ -603,12 +604,6 @@ ReadArrayStr(char **srcptr,
 	/* The caller already checked this */
 	Assert(**srcptr == '{');
 
-	if (!dimensions_specified)
-	{
-		/* Initialize dim[] entries to -1 meaning "unknown" */
-		for (int i = 0; i < MAXDIM; ++i)
-			dim[i] = -1;
-	}
 	ndim_frozen = dimensions_specified;
 
 	maxitems = 16;
@@ -675,10 +670,30 @@ ReadArrayStr(char **srcptr,
 				{
 					/* Nested sub-arrays count as elements of outer level */
 					nelems[nest_level - 1]++;
+
+					if (nitems > 0 && expect_delim == false)
+					{
+						ereturn(escontext, false,
+								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+								 errmsg("malformed array literal: \"%s\"", origStr),
+								 errdetail("Unexpected \"%c\" character.",
+										typdelim)));
+					}
 					expect_delim = true;
 				}
 				else
+				{
+					/* rightmost should precede with element or bracket */
+					if (nitems > 0 && expect_delim == false)
+					{
+						ereturn(escontext, false,
+								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+								errmsg("malformed array literal: \"%s\"", origStr),
+								errdetail("Unexpected \"%c\" character.",
+										typdelim)));
+					}
 					expect_delim = false;
+				}
 
 				if (dim[nest_level] < 0)
 				{
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 93e11068..e57590df 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1520,7 +1520,7 @@ select '{{1},{}}'::text[];
 ERROR:  malformed array literal: "{{1},{}}"
 LINE 1: select '{{1},{}}'::text[];
                ^
-DETAIL:  Multidimensional arrays must have sub-arrays with matching dimensions.
+DETAIL:  Unexpected "," character.
 select '{{},{1}}'::text[];
 ERROR:  malformed array literal: "{{},{1}}"
 LINE 1: select '{{},{1}}'::text[];
@@ -1544,6 +1544,26 @@ 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.
 -- 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 b7e2b180..d9805aa1 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -468,6 +468,10 @@ 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[];
 -- none of the above should be accepted
 
 -- all of the following should be accepted
-- 
2.34.1

Reply via email to