Further tightening of the array literal parser. Prevent junk
authorJoe Conway
Sat, 28 Aug 2004 19:31:29 +0000 (19:31 +0000)
committerJoe Conway
Sat, 28 Aug 2004 19:31:29 +0000 (19:31 +0000)
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
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index 8e46c2fde105ba7b356941aa654886531f56e2eb..6f9b5bfaa956b647ac85b8ce90c9bb78d6744d57 100644 (file)
@@ -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];
 
index ea8e2b6e8f9b38bd2006d0959d1f4a7469e2e085..083693ffa947bfa29ce6388c68e7ab60db3c7a80 100644 (file)
@@ -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
index 97cb5bbc075f455ec597ed8d966643d6ee9a8a8a..d9a3252d316025af1df7248a1d735131f092721b 100644 (file)
@@ -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