Improve error messages for malformed array input strings.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 Dec 2014 23:23:16 +0000 (18:23 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 2 Dec 2014 23:23:27 +0000 (18:23 -0500)
Make the error messages issued by array_in() uniformly follow the style
ERROR: malformed array literal: "actual input string"
DETAIL: specific complaint here
and rewrite many of the specific complaints to be clearer.

The immediate motivation for doing this is a complaint from Josh Berkus
that json_to_record() produced an unintelligible error message when
dealing with an array item, because it tries to feed the JSON-format
array value to array_in().  Really it ought to be smart enough to
perform JSON-to-Postgres array conversion, but that's a future feature
not a bug fix.  In the meantime, this change is something we agreed
we could back-patch into 9.4, and it should help de-confuse things a bit.

src/backend/utils/adt/arrayfuncs.c
src/pl/plperl/expected/plperl_array.out
src/test/regress/expected/arrays.out

index 743351b95e0abc2a570be15982ef34e3f1730a35..933c6b035c60beb17a754e2edfcab25ffc2b7c9f 100644 (file)
@@ -247,11 +247,13 @@ array_in(PG_FUNCTION_ARGS)
                                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                                                        ndim + 1, MAXDIM)));
 
-               for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
+               for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
+                        /* skip */ ;
                if (q == p)                             /* no digits? */
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("missing dimension value")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
 
                if (*q == ':')
                {
@@ -259,11 +261,13 @@ array_in(PG_FUNCTION_ARGS)
                        *q = '\0';
                        lBound[ndim] = atoi(p);
                        p = q + 1;
-                       for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
+                       for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++)
+                                /* skip */ ;
                        if (q == p)                     /* no digits? */
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                errmsg("missing dimension value")));
+                                                errmsg("malformed array literal: \"%s\"", string),
+                                                errdetail("Missing array dimension value.")));
                }
                else
                {
@@ -273,7 +277,9 @@ array_in(PG_FUNCTION_ARGS)
                if (*q != ']')
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("missing \"]\" in array dimensions")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("Missing \"%s\" after array dimensions.",
+                                                          "]")));
 
                *q = '\0';
                ub = atoi(p);
@@ -293,7 +299,8 @@ array_in(PG_FUNCTION_ARGS)
                if (*p != '{')
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("array value must start with \"{\" or dimension information")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("Array value must start with \"{\" or dimension information.")));
                ndim = ArrayCount(p, dim, typdelim);
                for (i = 0; i < ndim; i++)
                        lBound[i] = 1;
@@ -307,7 +314,9 @@ array_in(PG_FUNCTION_ARGS)
                if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("missing assignment operator")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("Missing \"%s\" after array dimensions.",
+                                                          ASSGN)));
                p += strlen(ASSGN);
                while (array_isspace(*p))
                        p++;
@@ -319,18 +328,21 @@ array_in(PG_FUNCTION_ARGS)
                if (*p != '{')
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("array value must start with \"{\" or dimension information")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("Array contents must start with \"{\".")));
                ndim_braces = ArrayCount(p, dim_braces, typdelim);
                if (ndim_braces != ndim)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                               errmsg("array dimensions incompatible with array literal")));
+                                        errmsg("malformed array literal: \"%s\"", string),
+                                        errdetail("Specified array dimensions do not match array contents.")));
                for (i = 0; i < ndim; ++i)
                {
                        if (dim[i] != dim_braces[i])
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                               errmsg("array dimensions incompatible with array literal")));
+                                                errmsg("malformed array literal: \"%s\"", string),
+                                                errdetail("Specified array dimensions do not match array contents.")));
                }
        }
 
@@ -460,7 +472,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                        /* Signal a premature end of the string */
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                        errmsg("malformed array literal: \"%s\"", str)));
+                                                        errmsg("malformed array literal: \"%s\"", str),
+                                                        errdetail("Unexpected end of input.")));
                                        break;
                                case '\\':
 
@@ -475,7 +488,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                parse_state != ARRAY_ELEM_DELIMITED)
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                                errdetail("Unexpected \"%c\" character.",
+                                                                                  '\\')));
                                        if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
                                                parse_state = ARRAY_ELEM_STARTED;
                                        /* skip the escaped character */
@@ -484,7 +499,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                        else
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                                errdetail("Unexpected end of input.")));
                                        break;
                                case '\"':
 
@@ -498,7 +514,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                parse_state != ARRAY_ELEM_DELIMITED)
                                                ereport(ERROR,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                                errdetail("Unexpected array element.")));
                                        in_quotes = !in_quotes;
                                        if (in_quotes)
                                                parse_state = ARRAY_QUOTED_ELEM_STARTED;
@@ -518,7 +535,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                        parse_state != ARRAY_LEVEL_DELIMITED)
                                                        ereport(ERROR,
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                               errdetail("Unexpected \"%c\" character.",
+                                                                                 '{')));
                                                parse_state = ARRAY_LEVEL_STARTED;
                                                if (nest_level >= MAXDIM)
                                                        ereport(ERROR,
@@ -546,21 +565,25 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                        !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
                                                        ereport(ERROR,
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                               errdetail("Unexpected \"%c\" character.",
+                                                                                 '}')));
                                                parse_state = ARRAY_LEVEL_COMPLETED;
                                                if (nest_level == 0)
                                                        ereport(ERROR,
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                       errmsg("malformed array literal: \"%s\"", str)));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                        errdetail("Unmatched \"%c\" character.", '}')));
                                                nest_level--;
 
                                                if (nelems_last[nest_level] != 0 &&
                                                        nelems[nest_level] != nelems_last[nest_level])
                                                        ereport(ERROR,
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                               errmsg("multidimensional arrays must have "
-                                                                          "array expressions with matching "
-                                                                          "dimensions")));
+                                                         errmsg("malformed array literal: \"%s\"", str),
+                                                               errdetail("Multidimensional arrays must have "
+                                                                                 "sub-arrays with matching "
+                                                                                 "dimensions.")));
                                                nelems_last[nest_level] = nelems[nest_level];
                                                nelems[nest_level] = 1;
                                                if (nest_level == 0)
@@ -591,7 +614,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                                parse_state != ARRAY_LEVEL_COMPLETED)
                                                                ereport(ERROR,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                                errmsg("malformed array literal: \"%s\"", str)));
+                                                                errmsg("malformed array literal: \"%s\"", str),
+                                                                errdetail("Unexpected \"%c\" character.",
+                                                                                  typdelim)));
                                                        if (parse_state == ARRAY_LEVEL_COMPLETED)
                                                                parse_state = ARRAY_LEVEL_DELIMITED;
                                                        else
@@ -612,7 +637,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
                                                                parse_state != ARRAY_ELEM_DELIMITED)
                                                                ereport(ERROR,
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                                                errmsg("malformed array literal: \"%s\"", str)));
+                                                                errmsg("malformed array literal: \"%s\"", str),
+                                                                errdetail("Unexpected array element.")));
                                                        parse_state = ARRAY_ELEM_STARTED;
                                                }
                                        }
@@ -631,7 +657,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
                if (!array_isspace(*ptr++))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-                                        errmsg("malformed array literal: \"%s\"", str)));
+                                        errmsg("malformed array literal: \"%s\"", str),
+                                        errdetail("Junk after closing right brace.")));
        }
 
        /* special case for an empty array */
@@ -718,7 +745,8 @@ ReadArrayStr(char *arrayStr,
         * character.
         *
         * The error checking in this routine is mostly pro-forma, since we expect
-        * that ArrayCount() already validated the string.
+        * that ArrayCount() already validated the string.  So we don't bother
+        * with errdetail messages.
         */
        srcptr = arrayStr;
        while (!eoArray)
index d21c84bd98a3d4ddea10d3ae2e8173ebbd33a2cc..6347b5211d27f8cedbec5b8cfc59be6c30eedcca 100644 (file)
@@ -65,9 +65,10 @@ ERROR:  number of array dimensions (7) exceeds the maximum allowed (6)
 LINE 1: select plperl_sum_array('{{{{{{{1,2},{3,4}},{{5,6},{7,8}}},{...
                                 ^
 select plperl_sum_array('{{{1,2,3}, {4,5,6,7}}, {{7,8,9}, {10, 11, 12}}}');
-ERROR:  multidimensional arrays must have array expressions with matching dimensions
+ERROR:  malformed array literal: "{{{1,2,3}, {4,5,6,7}}, {{7,8,9}, {10, 11, 12}}}"
 LINE 1: select plperl_sum_array('{{{1,2,3}, {4,5,6,7}}, {{7,8,9}, {1...
                                 ^
+DETAIL:  Multidimensional arrays must have sub-arrays with matching dimensions.
 CREATE OR REPLACE FUNCTION plperl_concat(TEXT[]) RETURNS TEXT AS $$
        my $array_arg = shift;
        my $result = "";
index cb606afd9cfe47b14dba1ae5011d1f806686252e..d33c9b90fffb22d1703a4e263b15f2d3aebdf4bb 100644 (file)
@@ -1065,26 +1065,32 @@ select '{{1,{2}},{2,3}}'::text[];
 ERROR:  malformed array literal: "{{1,{2}},{2,3}}"
 LINE 1: select '{{1,{2}},{2,3}}'::text[];
                ^
+DETAIL:  Unexpected "{" character.
 select '{{},{}}'::text[];
 ERROR:  malformed array literal: "{{},{}}"
 LINE 1: select '{{},{}}'::text[];
                ^
+DETAIL:  Unexpected "}" character.
 select E'{{1,2},\\{2,3}}'::text[];
 ERROR:  malformed array literal: "{{1,2},\{2,3}}"
 LINE 1: select E'{{1,2},\\{2,3}}'::text[];
                ^
+DETAIL:  Unexpected "\" character.
 select '{{"1 2" x},{3}}'::text[];
 ERROR:  malformed array literal: "{{"1 2" x},{3}}"
 LINE 1: select '{{"1 2" x},{3}}'::text[];
                ^
+DETAIL:  Unexpected array element.
 select '{}}'::text[];
 ERROR:  malformed array literal: "{}}"
 LINE 1: select '{}}'::text[];
                ^
+DETAIL:  Junk after closing right brace.
 select '{ }}'::text[];
 ERROR:  malformed array literal: "{ }}"
 LINE 1: select '{ }}'::text[];
                ^
+DETAIL:  Junk after closing right brace.
 select array[];
 ERROR:  cannot determine type of empty array
 LINE 1: select array[];