Commit 475aedd1 authored by Tom Lane's avatar Tom Lane

Improve error messages for malformed array input strings.

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.
parent 0fd38e13
...@@ -247,11 +247,13 @@ array_in(PG_FUNCTION_ARGS) ...@@ -247,11 +247,13 @@ array_in(PG_FUNCTION_ARGS)
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndim + 1, MAXDIM))); 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? */ if (q == p) /* no digits? */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing dimension value"))); errmsg("malformed array literal: \"%s\"", string),
errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
if (*q == ':') if (*q == ':')
{ {
...@@ -259,11 +261,13 @@ array_in(PG_FUNCTION_ARGS) ...@@ -259,11 +261,13 @@ array_in(PG_FUNCTION_ARGS)
*q = '\0'; *q = '\0';
lBound[ndim] = atoi(p); lBound[ndim] = atoi(p);
p = q + 1; 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? */ if (q == p) /* no digits? */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing dimension value"))); errmsg("malformed array literal: \"%s\"", string),
errdetail("Missing array dimension value.")));
} }
else else
{ {
...@@ -273,7 +277,9 @@ array_in(PG_FUNCTION_ARGS) ...@@ -273,7 +277,9 @@ array_in(PG_FUNCTION_ARGS)
if (*q != ']') if (*q != ']')
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("missing \"]\" in array dimensions"))); errmsg("malformed array literal: \"%s\"", string),
errdetail("Missing \"%s\" after array dimensions.",
"]")));
*q = '\0'; *q = '\0';
ub = atoi(p); ub = atoi(p);
...@@ -293,7 +299,8 @@ array_in(PG_FUNCTION_ARGS) ...@@ -293,7 +299,8 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{') if (*p != '{')
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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); ndim = ArrayCount(p, dim, typdelim);
for (i = 0; i < ndim; i++) for (i = 0; i < ndim; i++)
lBound[i] = 1; lBound[i] = 1;
...@@ -307,7 +314,9 @@ array_in(PG_FUNCTION_ARGS) ...@@ -307,7 +314,9 @@ array_in(PG_FUNCTION_ARGS)
if (strncmp(p, ASSGN, strlen(ASSGN)) != 0) if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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); p += strlen(ASSGN);
while (array_isspace(*p)) while (array_isspace(*p))
p++; p++;
...@@ -319,18 +328,21 @@ array_in(PG_FUNCTION_ARGS) ...@@ -319,18 +328,21 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{') if (*p != '{')
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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); ndim_braces = ArrayCount(p, dim_braces, typdelim);
if (ndim_braces != ndim) if (ndim_braces != ndim)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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) for (i = 0; i < ndim; ++i)
{ {
if (dim[i] != dim_braces[i]) if (dim[i] != dim_braces[i])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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) ...@@ -460,7 +472,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
/* Signal a premature end of the string */ /* Signal a premature end of the string */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", str))); errmsg("malformed array literal: \"%s\"", str),
errdetail("Unexpected end of input.")));
break; break;
case '\\': case '\\':
...@@ -475,7 +488,9 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -475,7 +488,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED) parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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) if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
parse_state = ARRAY_ELEM_STARTED; parse_state = ARRAY_ELEM_STARTED;
/* skip the escaped character */ /* skip the escaped character */
...@@ -484,7 +499,8 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -484,7 +499,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", str))); errmsg("malformed array literal: \"%s\"", str),
errdetail("Unexpected end of input.")));
break; break;
case '\"': case '\"':
...@@ -498,7 +514,8 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -498,7 +514,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED) parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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; in_quotes = !in_quotes;
if (in_quotes) if (in_quotes)
parse_state = ARRAY_QUOTED_ELEM_STARTED; parse_state = ARRAY_QUOTED_ELEM_STARTED;
...@@ -518,7 +535,9 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -518,7 +535,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_LEVEL_DELIMITED) parse_state != ARRAY_LEVEL_DELIMITED)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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; parse_state = ARRAY_LEVEL_STARTED;
if (nest_level >= MAXDIM) if (nest_level >= MAXDIM)
ereport(ERROR, ereport(ERROR,
...@@ -546,21 +565,25 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -546,21 +565,25 @@ ArrayCount(const char *str, int *dim, char typdelim)
!(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED)) !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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; parse_state = ARRAY_LEVEL_COMPLETED;
if (nest_level == 0) if (nest_level == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed array literal: \"%s\"", str))); errmsg("malformed array literal: \"%s\"", str),
errdetail("Unmatched \"%c\" character.", '}')));
nest_level--; nest_level--;
if (nelems_last[nest_level] != 0 && if (nelems_last[nest_level] != 0 &&
nelems[nest_level] != nelems_last[nest_level]) nelems[nest_level] != nelems_last[nest_level])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("multidimensional arrays must have " errmsg("malformed array literal: \"%s\"", str),
"array expressions with matching " errdetail("Multidimensional arrays must have "
"dimensions"))); "sub-arrays with matching "
"dimensions.")));
nelems_last[nest_level] = nelems[nest_level]; nelems_last[nest_level] = nelems[nest_level];
nelems[nest_level] = 1; nelems[nest_level] = 1;
if (nest_level == 0) if (nest_level == 0)
...@@ -591,7 +614,9 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -591,7 +614,9 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_LEVEL_COMPLETED) parse_state != ARRAY_LEVEL_COMPLETED)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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) if (parse_state == ARRAY_LEVEL_COMPLETED)
parse_state = ARRAY_LEVEL_DELIMITED; parse_state = ARRAY_LEVEL_DELIMITED;
else else
...@@ -612,7 +637,8 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -612,7 +637,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
parse_state != ARRAY_ELEM_DELIMITED) parse_state != ARRAY_ELEM_DELIMITED)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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; parse_state = ARRAY_ELEM_STARTED;
} }
} }
...@@ -631,7 +657,8 @@ ArrayCount(const char *str, int *dim, char typdelim) ...@@ -631,7 +657,8 @@ ArrayCount(const char *str, int *dim, char typdelim)
if (!array_isspace(*ptr++)) if (!array_isspace(*ptr++))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (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 */ /* special case for an empty array */
...@@ -718,7 +745,8 @@ ReadArrayStr(char *arrayStr, ...@@ -718,7 +745,8 @@ ReadArrayStr(char *arrayStr,
* character. * character.
* *
* The error checking in this routine is mostly pro-forma, since we expect * 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; srcptr = arrayStr;
while (!eoArray) while (!eoArray)
......
...@@ -65,9 +65,10 @@ ERROR: number of array dimensions (7) exceeds the maximum allowed (6) ...@@ -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}}},{... 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}}}'); 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... 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 $$ CREATE OR REPLACE FUNCTION plperl_concat(TEXT[]) RETURNS TEXT AS $$
my $array_arg = shift; my $array_arg = shift;
my $result = ""; my $result = "";
......
...@@ -1065,26 +1065,32 @@ select '{{1,{2}},{2,3}}'::text[]; ...@@ -1065,26 +1065,32 @@ select '{{1,{2}},{2,3}}'::text[];
ERROR: malformed array literal: "{{1,{2}},{2,3}}" ERROR: malformed array literal: "{{1,{2}},{2,3}}"
LINE 1: select '{{1,{2}},{2,3}}'::text[]; LINE 1: select '{{1,{2}},{2,3}}'::text[];
^ ^
DETAIL: Unexpected "{" character.
select '{{},{}}'::text[]; select '{{},{}}'::text[];
ERROR: malformed array literal: "{{},{}}" ERROR: malformed array literal: "{{},{}}"
LINE 1: select '{{},{}}'::text[]; LINE 1: select '{{},{}}'::text[];
^ ^
DETAIL: Unexpected "}" character.
select E'{{1,2},\\{2,3}}'::text[]; select E'{{1,2},\\{2,3}}'::text[];
ERROR: malformed array literal: "{{1,2},\{2,3}}" ERROR: malformed array literal: "{{1,2},\{2,3}}"
LINE 1: select E'{{1,2},\\{2,3}}'::text[]; LINE 1: select E'{{1,2},\\{2,3}}'::text[];
^ ^
DETAIL: Unexpected "\" character.
select '{{"1 2" x},{3}}'::text[]; select '{{"1 2" x},{3}}'::text[];
ERROR: malformed array literal: "{{"1 2" x},{3}}" ERROR: malformed array literal: "{{"1 2" x},{3}}"
LINE 1: select '{{"1 2" x},{3}}'::text[]; LINE 1: select '{{"1 2" x},{3}}'::text[];
^ ^
DETAIL: Unexpected array element.
select '{}}'::text[]; select '{}}'::text[];
ERROR: malformed array literal: "{}}" ERROR: malformed array literal: "{}}"
LINE 1: select '{}}'::text[]; LINE 1: select '{}}'::text[];
^ ^
DETAIL: Junk after closing right brace.
select '{ }}'::text[]; select '{ }}'::text[];
ERROR: malformed array literal: "{ }}" ERROR: malformed array literal: "{ }}"
LINE 1: select '{ }}'::text[]; LINE 1: select '{ }}'::text[];
^ ^
DETAIL: Junk after closing right brace.
select array[]; select array[];
ERROR: cannot determine type of empty array ERROR: cannot determine type of empty array
LINE 1: select array[]; LINE 1: select array[];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment