From f900af7961fb7009242dbcaf3b484d4b1ed8752d Mon Sep 17 00:00:00 2001
From: Joe Conway <mail@joeconway.com>
Date: Sat, 28 Aug 2004 19:31:29 +0000
Subject: [PATCH] Further tightening of the array literal parser. Prevent junk
 from being accepted after the outer right brace. Per report from Markus
 Bertheau.

Also add regression test cases for this change, and for previous
recent array literal parser changes.
---
 src/backend/utils/adt/arrayfuncs.c   | 30 +++++++++++---
 src/test/regress/expected/arrays.out | 58 ++++++++++++++++++++++++++++
 src/test/regress/sql/arrays.sql      | 25 ++++++++++++
 3 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 8e46c2fde10..6f9b5bfaa95 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.107 2004/08/08 05:01:55 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.108 2004/08/28 19:31:28 joe Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -183,9 +183,7 @@ array_in(PG_FUNCTION_ARGS)
 	typioparam = my_extra->typioparam;
 
 	/* Make a modifiable copy of the input */
-	/* XXX why are we allocating an extra 2 bytes here? */
-	string_save = (char *) palloc(strlen(string) + 3);
-	strcpy(string_save, string);
+	string_save = pstrdup(string);
 
 	/*
 	 * If the input string starts with dimension info, read and use that.
@@ -375,6 +373,7 @@ ArrayCount(char *str, int *dim, char typdelim)
 					nelems_last[MAXDIM];
 	bool			scanning_string = false;
 	bool			eoArray = false;
+	bool			empty_array = true;
 	char		   *ptr;
 	ArrayParseState	parse_state = ARRAY_NO_LEVEL;
 
@@ -385,7 +384,7 @@ ArrayCount(char *str, int *dim, char typdelim)
 	}
 
 	/* special case for an empty array */
-	if (strncmp(str, "{}", 2) == 0)
+	if (strcmp(str, "{}") == 0)
 		return 0;
 
 	ptr = str;
@@ -395,6 +394,10 @@ ArrayCount(char *str, int *dim, char typdelim)
 
 		while (!itemdone)
 		{
+			if (parse_state == ARRAY_ELEM_STARTED ||
+				parse_state == ARRAY_QUOTED_ELEM_STARTED)
+				empty_array = false;
+			
 			switch (*ptr)
 			{
 				case '\0':
@@ -481,7 +484,8 @@ ArrayCount(char *str, int *dim, char typdelim)
 						if (parse_state != ARRAY_ELEM_STARTED &&
 							parse_state != ARRAY_ELEM_COMPLETED &&
 							parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
-							parse_state != ARRAY_LEVEL_COMPLETED)
+							parse_state != ARRAY_LEVEL_COMPLETED &&
+							!(nest_level == 1 &&  parse_state == ARRAY_LEVEL_STARTED))
 							ereport(ERROR,
 								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 								errmsg("malformed array literal: \"%s\"", str)));
@@ -562,6 +566,20 @@ ArrayCount(char *str, int *dim, char typdelim)
 		temp[ndim - 1]++;
 		ptr++;
 	}
+	
+	/* only whitespace is allowed after the closing brace */
+	while (*ptr)
+	{
+		if (!isspace(*ptr++))
+			ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				errmsg("malformed array literal: \"%s\"", str)));
+	}
+	
+	/* special case for an empty array */
+	if (empty_array)
+		return 0;
+		
 	for (i = 0; i < ndim; ++i)
 		dim[i] = temp[i];
 
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index ea8e2b6e8f9..083693ffa94 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -425,3 +425,61 @@ select 'foo' ilike all (array['F%', '%O']); -- t
  t
 (1 row)
 
+--
+-- General array parser tests
+--
+-- none of the following should be accepted
+select '{{1,{2}},{2,3}}'::text[];
+ERROR:  malformed array literal: "{{1,{2}},{2,3}}"
+select '{{},{}}'::text[];
+ERROR:  malformed array literal: "{{},{}}"
+select '{{1,2},\\{2,3}}'::text[];
+ERROR:  malformed array literal: "{{1,2},\{2,3}}"
+select '{{"1 2" x},{3}}'::text[];
+ERROR:  malformed array literal: "{{"1 2" x},{3}}"
+select '{}}'::text[];
+ERROR:  malformed array literal: "{}}"
+select '{ }}'::text[];
+ERROR:  malformed array literal: "{ }}"
+-- none of the above should be accepted
+-- all of the following should be accepted
+select '{}'::text[];
+ text 
+------
+ {}
+(1 row)
+
+select '{{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}'::text[];
+                     text                      
+-----------------------------------------------
+ {{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}
+(1 row)
+
+select '{0 second  ,0 second}'::interval[];
+   interval    
+---------------
+ {"@ 0","@ 0"}
+(1 row)
+
+select '{ { "," } , { 3 } }'::text[];
+    text     
+-------------
+ {{","},{3}}
+(1 row)
+
+select '  {   {  "  0 second  "   ,  0 second  }   }'::text[];
+             text              
+-------------------------------
+ {{"  0 second  ","0 second"}}
+(1 row)
+
+select '{
+           0 second,
+           @ 1 hour @ 42 minutes @ 20 seconds
+         }'::interval[];
+              interval              
+------------------------------------
+ {"@ 0","@ 1 hour 42 mins 20 secs"}
+(1 row)
+
+-- all of the above should be accepted
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 97cb5bbc075..d9a3252d316 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -192,3 +192,28 @@ select 'foo' not like any (array['%a', '%b']); -- t
 select 'foo' not like all (array['%a', '%o']); -- f
 select 'foo' ilike any (array['%A', '%O']); -- t
 select 'foo' ilike all (array['F%', '%O']); -- t
+
+--
+-- General array parser tests
+--
+
+-- none of the following should be accepted
+select '{{1,{2}},{2,3}}'::text[];
+select '{{},{}}'::text[];
+select '{{1,2},\\{2,3}}'::text[];
+select '{{"1 2" x},{3}}'::text[];
+select '{}}'::text[];
+select '{ }}'::text[];
+-- none of the above should be accepted
+
+-- all of the following should be accepted
+select '{}'::text[];
+select '{{{1,2,3,4},{2,3,4,5}},{{3,4,5,6},{4,5,6,7}}}'::text[];
+select '{0 second  ,0 second}'::interval[];
+select '{ { "," } , { 3 } }'::text[];
+select '  {   {  "  0 second  "   ,  0 second  }   }'::text[];
+select '{
+           0 second,
+           @ 1 hour @ 42 minutes @ 20 seconds
+         }'::interval[];
+-- all of the above should be accepted
-- 
GitLab