From 653aa603f501aa6e4865105a928cd13082ee7152 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 11 Dec 2020 17:54:10 -0500 Subject: [PATCH] Provide an error cursor for "can't subscript" error messages. Commit c7aba7c14 didn't add this, but after more fooling with the feature I feel that it'd be useful. To make this possible, refactor getSubscriptingRoutines() so that the caller is responsible for throwing any error. (In clauses.c, I just chose to make the most conservative assumption rather than throwing an error. We don't expect failures there anyway really, so the code space for an error message would be a poor investment.) --- src/backend/executor/execExpr.c | 8 ++++++++ src/backend/optimizer/util/clauses.c | 5 +++-- src/backend/parser/parse_node.c | 6 ++++++ src/backend/utils/cache/lsyscache.c | 7 ++----- src/test/regress/expected/arrays.out | 2 ++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 0134ecc261..5c3210a9cc 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -2536,6 +2536,14 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref, /* Look up the subscripting support methods */ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype, NULL); + if (!sbsroutines) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot subscript type %s because it does not support subscripting", + format_type_be(sbsref->refcontainertype)), + state->parent ? + executor_errposition(state->parent->state, + exprLocation((Node *) sbsref)) : 0)); /* Allocate sbsrefstate, with enough space for per-subscript arrays too */ sbsrefstate = palloc0(MAXALIGN(sizeof(SubscriptingRefState)) + diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e3a81a7a02..8f5cbf99f4 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -848,7 +848,7 @@ contain_nonstrict_functions_walker(Node *node, void *context) return true; /* Otherwise we must look up the subscripting support methods */ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype, NULL); - if (!sbsroutines->fetch_strict) + if (!(sbsroutines && sbsroutines->fetch_strict)) return true; /* else fall through to check args */ } @@ -1144,7 +1144,8 @@ contain_leaked_vars_walker(Node *node, void *context) /* Consult the subscripting support method info */ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype, NULL); - if (!(sbsref->refassgnexpr != NULL ? + if (!sbsroutines || + !(sbsref->refassgnexpr != NULL ? sbsroutines->store_leakproof : sbsroutines->fetch_leakproof)) { diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index e90f6c9d01..3e20dfff2e 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -271,6 +271,12 @@ transformContainerSubscripts(ParseState *pstate, * functions and typelem. */ sbsroutines = getSubscriptingRoutines(containerType, &elementType); + if (!sbsroutines) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot subscript type %s because it does not support subscripting", + format_type_be(containerType)), + parser_errposition(pstate, exprLocation(containerBase)))); /* * Detect whether any of the indirection items are slice specifiers. diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 7dd4945446..204bcd03c0 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3031,7 +3031,7 @@ get_typsubscript(Oid typid, Oid *typelemp) * getSubscriptingRoutines * * Given the type OID, fetch the type's subscripting methods struct. - * Fail if type is not subscriptable. + * Return NULL if type is not subscriptable. * * If typelemp isn't NULL, we also store the type's typelem value there. * This saves some callers an extra catalog lookup. @@ -3042,10 +3042,7 @@ getSubscriptingRoutines(Oid typid, Oid *typelemp) RegProcedure typsubscript = get_typsubscript(typid, typelemp); if (!OidIsValid(typsubscript)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot subscript type %s because it does not support subscripting", - format_type_be(typid)))); + return NULL; return (const struct SubscriptRoutines *) DatumGetPointer(OidFunctionCall0(typsubscript)); diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 0254fac801..8bc7721e7d 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -238,6 +238,8 @@ ERROR: array subscript in assignment must not be null -- Un-subscriptable type SELECT (now())[1]; ERROR: cannot subscript type timestamp with time zone because it does not support subscripting +LINE 1: SELECT (now())[1]; + ^ -- test slices with empty lower and/or upper index CREATE TEMP TABLE arrtest_s ( a int2[],