diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 431 |
1 files changed, 265 insertions, 166 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2146329ad89..1e76d3b546f 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.246 2009/10/27 17:11:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.247 2009/10/31 01:41:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,8 +60,8 @@ static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); static Node *transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); -static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, - char *relname, int location); +static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, + int location); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); @@ -327,11 +327,64 @@ transformExpr(ParseState *pstate, Node *expr) return result; } +/* + * helper routine for delivering "column does not exist" error message + * + * (Usually we don't have to work this hard, but the general case of field + * selection from an arbitrary node needs it.) + */ +static void +unknown_attribute(ParseState *pstate, Node *relref, char *attname, + int location) +{ + RangeTblEntry *rte; + + if (IsA(relref, Var) && + ((Var *) relref)->varattno == InvalidAttrNumber) + { + /* Reference the RTE by alias not by actual table name */ + rte = GetRTEByRangeTablePosn(pstate, + ((Var *) relref)->varno, + ((Var *) relref)->varlevelsup); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %s.%s does not exist", + rte->eref->aliasname, attname), + parser_errposition(pstate, location))); + } + else + { + /* Have to do it by reference to the type of the expression */ + Oid relTypeId = exprType(relref); + + if (ISCOMPLEX(relTypeId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" not found in data type %s", + attname, format_type_be(relTypeId)), + parser_errposition(pstate, location))); + else if (relTypeId == RECORDOID) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("could not identify column \"%s\" in record data type", + attname), + parser_errposition(pstate, location))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("column notation .%s applied to type %s, " + "which is not a composite type", + attname, format_type_be(relTypeId)), + parser_errposition(pstate, location))); + } +} + static Node * transformIndirection(ParseState *pstate, Node *basenode, List *indirection) { Node *result = basenode; List *subscripts = NIL; + int location = exprLocation(basenode); ListCell *i; /* @@ -350,10 +403,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row expansion via \"*\" is not supported here"), - parser_errposition(pstate, exprLocation(basenode)))); + parser_errposition(pstate, location))); } else { + Node *newresult; + Assert(IsA(n, String)); /* process subscripts before this field selection */ @@ -367,11 +422,14 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) NULL); subscripts = NIL; - result = ParseFuncOrColumn(pstate, - list_make1(n), - list_make1(result), - false, false, false, - NULL, true, -1); + newresult = ParseFuncOrColumn(pstate, + list_make1(n), + list_make1(result), + false, false, false, + NULL, true, location); + if (newresult == NULL) + unknown_attribute(pstate, result, strVal(n), location); + result = newresult; } } /* process trailing subscripts, if any */ @@ -387,12 +445,37 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) return result; } +/* + * Transform a ColumnRef. + * + * If you find yourself changing this code, see also ExpandColumnRefStar. + */ static Node * transformColumnRef(ParseState *pstate, ColumnRef *cref) { - int numnames = list_length(cref->fields); - Node *node; + Node *node = NULL; + char *nspname = NULL; + char *relname = NULL; + char *colname = NULL; + RangeTblEntry *rte; int levels_up; + enum { + CRERR_NO_COLUMN, + CRERR_NO_RTE, + CRERR_WRONG_DB, + CRERR_TOO_MANY + } crerr = CRERR_NO_COLUMN; + + /* + * Give the PreParseColumnRefHook, if any, first shot. If it returns + * non-null then that's all, folks. + */ + if (pstate->p_pre_columnref_hook != NULL) + { + node = (*pstate->p_pre_columnref_hook) (pstate, cref); + if (node != NULL) + return node; + } /*---------- * The allowed syntaxes are: @@ -417,18 +500,17 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * database name; we check it here and then discard it. *---------- */ - switch (numnames) + switch (list_length(cref->fields)) { case 1: { Node *field1 = (Node *) linitial(cref->fields); - char *name1; Assert(IsA(field1, String)); - name1 = strVal(field1); + colname = strVal(field1); /* Try to identify as an unqualified column */ - node = colNameToVar(pstate, name1, false, cref->location); + node = colNameToVar(pstate, colname, false, cref->location); if (node == NULL) { @@ -441,7 +523,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * have used VALUE as a column name in the past.) */ if (pstate->p_value_substitute != NULL && - strcmp(name1, "value") == 0) + strcmp(colname, "value") == 0) { node = (Node *) copyObject(pstate->p_value_substitute); @@ -464,17 +546,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * PostQUEL-inspired syntax. The preferred form now is * "rel.*". */ - if (refnameRangeTblEntry(pstate, NULL, name1, - cref->location, - &levels_up) != NULL) - node = transformWholeRowRef(pstate, NULL, name1, + rte = refnameRangeTblEntry(pstate, NULL, colname, + cref->location, + &levels_up); + if (rte) + node = transformWholeRowRef(pstate, rte, cref->location); - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" does not exist", - name1), - parser_errposition(pstate, cref->location))); } break; } @@ -482,36 +559,38 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) { Node *field1 = (Node *) linitial(cref->fields); Node *field2 = (Node *) lsecond(cref->fields); - char *name1; - char *name2; Assert(IsA(field1, String)); - name1 = strVal(field1); + relname = strVal(field1); + + /* Locate the referenced RTE */ + rte = refnameRangeTblEntry(pstate, nspname, relname, + cref->location, + &levels_up); + if (rte == NULL) + { + crerr = CRERR_NO_RTE; + break; + } /* Whole-row reference? */ if (IsA(field2, A_Star)) { - node = transformWholeRowRef(pstate, NULL, name1, - cref->location); + node = transformWholeRowRef(pstate, rte, cref->location); break; } Assert(IsA(field2, String)); - name2 = strVal(field2); + colname = strVal(field2); - /* Try to identify as a once-qualified column */ - node = qualifiedNameToVar(pstate, NULL, name1, name2, - cref->location); + /* Try to identify as a column of the RTE */ + node = scanRTEForColumn(pstate, rte, colname, cref->location); if (node == NULL) { - /* - * Not known as a column of any range-table entry, so try - * it as a function call. - */ - node = transformWholeRowRef(pstate, NULL, name1, - cref->location); + /* Try it as a function call on the whole row */ + node = transformWholeRowRef(pstate, rte, cref->location); node = ParseFuncOrColumn(pstate, - list_make1(makeString(name2)), + list_make1(makeString(colname)), list_make1(node), false, false, false, NULL, true, cref->location); @@ -523,36 +602,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) Node *field1 = (Node *) linitial(cref->fields); Node *field2 = (Node *) lsecond(cref->fields); Node *field3 = (Node *) lthird(cref->fields); - char *name1; - char *name2; - char *name3; Assert(IsA(field1, String)); - name1 = strVal(field1); + nspname = strVal(field1); Assert(IsA(field2, String)); - name2 = strVal(field2); + relname = strVal(field2); + + /* Locate the referenced RTE */ + rte = refnameRangeTblEntry(pstate, nspname, relname, + cref->location, + &levels_up); + if (rte == NULL) + { + crerr = CRERR_NO_RTE; + break; + } /* Whole-row reference? */ if (IsA(field3, A_Star)) { - node = transformWholeRowRef(pstate, name1, name2, - cref->location); + node = transformWholeRowRef(pstate, rte, cref->location); break; } Assert(IsA(field3, String)); - name3 = strVal(field3); + colname = strVal(field3); - /* Try to identify as a twice-qualified column */ - node = qualifiedNameToVar(pstate, name1, name2, name3, - cref->location); + /* Try to identify as a column of the RTE */ + node = scanRTEForColumn(pstate, rte, colname, cref->location); if (node == NULL) { - /* Try it as a function call */ - node = transformWholeRowRef(pstate, name1, name2, - cref->location); + /* Try it as a function call on the whole row */ + node = transformWholeRowRef(pstate, rte, cref->location); node = ParseFuncOrColumn(pstate, - list_make1(makeString(name3)), + list_make1(makeString(colname)), list_make1(node), false, false, false, NULL, true, cref->location); @@ -565,49 +648,52 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) Node *field2 = (Node *) lsecond(cref->fields); Node *field3 = (Node *) lthird(cref->fields); Node *field4 = (Node *) lfourth(cref->fields); - char *name1; - char *name2; - char *name3; - char *name4; + char *catname; Assert(IsA(field1, String)); - name1 = strVal(field1); + catname = strVal(field1); Assert(IsA(field2, String)); - name2 = strVal(field2); + nspname = strVal(field2); Assert(IsA(field3, String)); - name3 = strVal(field3); + relname = strVal(field3); /* * We check the catalog name and then ignore it. */ - if (strcmp(name1, get_database_name(MyDatabaseId)) != 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cross-database references are not implemented: %s", - NameListToString(cref->fields)), - parser_errposition(pstate, cref->location))); + if (strcmp(catname, get_database_name(MyDatabaseId)) != 0) + { + crerr = CRERR_WRONG_DB; + break; + } + + /* Locate the referenced RTE */ + rte = refnameRangeTblEntry(pstate, nspname, relname, + cref->location, + &levels_up); + if (rte == NULL) + { + crerr = CRERR_NO_RTE; + break; + } /* Whole-row reference? */ if (IsA(field4, A_Star)) { - node = transformWholeRowRef(pstate, name2, name3, - cref->location); + node = transformWholeRowRef(pstate, rte, cref->location); break; } Assert(IsA(field4, String)); - name4 = strVal(field4); + colname = strVal(field4); - /* Try to identify as a twice-qualified column */ - node = qualifiedNameToVar(pstate, name2, name3, name4, - cref->location); + /* Try to identify as a column of the RTE */ + node = scanRTEForColumn(pstate, rte, colname, cref->location); if (node == NULL) { - /* Try it as a function call */ - node = transformWholeRowRef(pstate, name2, name3, - cref->location); + /* Try it as a function call on the whole row */ + node = transformWholeRowRef(pstate, rte, cref->location); node = ParseFuncOrColumn(pstate, - list_make1(makeString(name4)), + list_make1(makeString(colname)), list_make1(node), false, false, false, NULL, true, cref->location); @@ -615,86 +701,101 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) break; } default: - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("improper qualified name (too many dotted names): %s", - NameListToString(cref->fields)), - parser_errposition(pstate, cref->location))); - node = NULL; /* keep compiler quiet */ + crerr = CRERR_TOO_MANY; /* too many dotted names */ break; } - return node; -} - -/* - * Locate the parameter type info for the given parameter number, and - * return a pointer to it. - */ -static Oid * -find_param_type(ParseState *pstate, int paramno, int location) -{ - Oid *result; - /* - * Find topmost ParseState, which is where paramtype info lives. + * Now give the PostParseColumnRefHook, if any, a chance. We pass the + * translation-so-far so that it can throw an error if it wishes in the + * case that it has a conflicting interpretation of the ColumnRef. + * (If it just translates anyway, we'll throw an error, because we can't + * undo whatever effects the preceding steps may have had on the pstate.) + * If it returns NULL, use the standard translation, or throw a suitable + * error if there is none. */ - while (pstate->parentParseState != NULL) - pstate = pstate->parentParseState; - - /* Check parameter number is in range */ - if (paramno <= 0) /* probably can't happen? */ - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", paramno), - parser_errposition(pstate, location))); - if (paramno > pstate->p_numparams) + if (pstate->p_post_columnref_hook != NULL) { - if (!pstate->p_variableparams) + Node *hookresult; + + hookresult = (*pstate->p_post_columnref_hook) (pstate, cref, node); + if (node == NULL) + node = hookresult; + else if (hookresult != NULL) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", paramno), - parser_errposition(pstate, location))); - /* Okay to enlarge param array */ - if (pstate->p_paramtypes) - pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes, - paramno * sizeof(Oid)); - else - pstate->p_paramtypes = (Oid *) palloc(paramno * sizeof(Oid)); - /* Zero out the previously-unreferenced slots */ - MemSet(pstate->p_paramtypes + pstate->p_numparams, - 0, - (paramno - pstate->p_numparams) * sizeof(Oid)); - pstate->p_numparams = paramno; + (errcode(ERRCODE_AMBIGUOUS_COLUMN), + errmsg("column reference \"%s\" is ambiguous", + NameListToString(cref->fields)), + parser_errposition(pstate, cref->location))); } - result = &pstate->p_paramtypes[paramno - 1]; - - if (pstate->p_variableparams) + /* + * Throw error if no translation found. + */ + if (node == NULL) { - /* If not seen before, initialize to UNKNOWN type */ - if (*result == InvalidOid) - *result = UNKNOWNOID; + switch (crerr) + { + case CRERR_NO_COLUMN: + if (relname) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %s.%s does not exist", + relname, colname), + parser_errposition(pstate, cref->location))); + + else + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" does not exist", + colname), + parser_errposition(pstate, cref->location))); + break; + case CRERR_NO_RTE: + errorMissingRTE(pstate, makeRangeVar(nspname, relname, + cref->location)); + break; + case CRERR_WRONG_DB: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cross-database references are not implemented: %s", + NameListToString(cref->fields)), + parser_errposition(pstate, cref->location))); + break; + case CRERR_TOO_MANY: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("improper qualified name (too many dotted names): %s", + NameListToString(cref->fields)), + parser_errposition(pstate, cref->location))); + break; + } } - return result; + return node; } static Node * transformParamRef(ParseState *pstate, ParamRef *pref) { - int paramno = pref->number; - Oid *pptype = find_param_type(pstate, paramno, pref->location); - Param *param; - - param = makeNode(Param); - param->paramkind = PARAM_EXTERN; - param->paramid = paramno; - param->paramtype = *pptype; - param->paramtypmod = -1; - param->location = pref->location; - - return (Node *) param; + Node *result; + + /* + * The core parser knows nothing about Params. If a hook is supplied, + * call it. If not, or if the hook returns NULL, throw a generic error. + */ + if (pstate->p_paramref_hook != NULL) + result = (*pstate->p_paramref_hook) (pstate, pref); + else + result = NULL; + + if (result == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_PARAMETER), + errmsg("there is no parameter $%d", pref->number), + parser_errposition(pstate, pref->location))); + + return result; } /* Test whether an a_expr is a plain NULL constant or not */ @@ -1861,26 +1962,33 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) &sublevels_up); Assert(sublevels_up == 0); - /* If a parameter is used, it must be of type REFCURSOR */ + /* + * If a parameter is used, it must be of type REFCURSOR. To verify + * that the parameter hooks think so, build a dummy ParamRef and + * transform it. + */ if (cexpr->cursor_name == NULL) { - Oid *pptype = find_param_type(pstate, cexpr->cursor_param, -1); - - if (pstate->p_variableparams && *pptype == UNKNOWNOID) - { - /* resolve unknown param type as REFCURSOR */ - *pptype = REFCURSOROID; - } - else if (*pptype != REFCURSOROID) - { + ParamRef *p = makeNode(ParamRef); + Node *n; + + p->number = cexpr->cursor_param; + p->location = -1; + n = transformParamRef(pstate, p); + /* Allow the parameter type to be inferred if it's unknown */ + if (exprType(n) == UNKNOWNOID) + n = coerce_type(pstate, n, UNKNOWNOID, + REFCURSOROID, -1, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, + -1); + if (exprType(n) != REFCURSOROID) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_PARAMETER), errmsg("inconsistent types deduced for parameter $%d", cexpr->cursor_param), errdetail("%s versus %s", - format_type_be(*pptype), + format_type_be(exprType(n)), format_type_be(REFCURSOROID)))); - } } return (Node *) cexpr; @@ -1896,23 +2004,14 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) * a rowtype; either a named composite type, or RECORD. */ static Node * -transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, - int location) +transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) { Var *result; - RangeTblEntry *rte; int vnum; int sublevels_up; Oid toid; - /* Look up the referenced RTE, failing if not present */ - - rte = refnameRangeTblEntry(pstate, schemaname, relname, location, - &sublevels_up); - - if (rte == NULL) - errorMissingRTE(pstate, - makeRangeVar(schemaname, relname, location)); + /* Find the RTE's rangetable location */ vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); |