diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 612 |
1 files changed, 412 insertions, 200 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 08e062d311..5d4a3244c1 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -3,18 +3,21 @@ * parse_expr.c * handle expressions in parser * - * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.241 2009/06/11 14:49:00 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.256 2010/07/06 19:18:57 momjian Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_attrdef.h" +#include "catalog/pg_constraint.h" +#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "miscadmin.h" @@ -30,6 +33,7 @@ #include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/xml.h" @@ -60,8 +64,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); @@ -159,23 +163,28 @@ transformExpr(ParseState *pstate, Node *expr) Oid elementType; int32 targetTypmod; - targetType = typenameTypeId(pstate, tc->typename, + targetType = typenameTypeId(pstate, tc->typeName, &targetTypmod); elementType = get_element_type(targetType); if (OidIsValid(elementType)) { - result = transformArrayExpr(pstate, - (A_ArrayExpr *) tc->arg, - targetType, - elementType, - targetTypmod); - break; + /* + * tranformArrayExpr doesn't know how to check domain + * constraints, so ask it to return the base type + * instead. transformTypeCast below will cast it to + * the domain. In the usual case that the target is + * not a domain, transformTypeCast is a no-op. + */ + targetType = getBaseTypeAndTypmod(targetType, + &targetTypmod); + + tc = copyObject(tc); + tc->arg = transformArrayExpr(pstate, + (A_ArrayExpr *) tc->arg, + targetType, + elementType, + targetTypmod); } - - /* - * Corner case: ARRAY[] cast to a non-array type. Fall - * through to do it the standard way. - */ } result = transformTypeCast(pstate, tc); @@ -228,6 +237,15 @@ transformExpr(ParseState *pstate, Node *expr) result = transformFuncCall(pstate, (FuncCall *) expr); break; + case T_NamedArgExpr: + { + NamedArgExpr *na = (NamedArgExpr *) expr; + + na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg); + result = expr; + break; + } + case T_SubLink: result = transformSubLink(pstate, (SubLink *) expr); break; @@ -262,6 +280,7 @@ transformExpr(ParseState *pstate, Node *expr) n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg); /* the argument can be any type, so don't coerce it */ + n->argisrow = type_is_rowtype(exprType((Node *) n->arg)); result = expr; break; } @@ -301,6 +320,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: case T_CaseTestExpr: + case T_ArrayExpr: case T_CoerceToDomain: case T_CoerceToDomainValue: case T_SetToDefault: @@ -318,11 +338,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; /* @@ -341,10 +414,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 */ @@ -358,11 +433,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), + NIL, false, false, false, + NULL, true, location); + if (newresult == NULL) + unknown_attribute(pstate, result, strVal(n), location); + result = newresult; } } /* process trailing subscripts, if any */ @@ -378,12 +456,38 @@ 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: @@ -408,18 +512,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) { @@ -432,7 +535,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); @@ -455,17 +558,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; } @@ -473,39 +571,40 @@ 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, true, - 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. Here, we will create an - * implicit RTE for tables not already entered. - */ - 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, + NIL, false, false, false, NULL, true, cref->location); } break; @@ -515,38 +614,42 @@ 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, true, - 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, + NIL, false, false, false, NULL, true, cref->location); } break; @@ -557,136 +660,154 @@ 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, true, - 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, + NIL, false, false, false, NULL, true, cref->location); } 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 */ @@ -1093,6 +1214,7 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) { List *targs; ListCell *args; + Node *result; /* Transform the list of arguments ... */ targs = NIL; @@ -1103,15 +1225,97 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) } /* ... and hand off to ParseFuncOrColumn */ - return ParseFuncOrColumn(pstate, - fn->funcname, - targs, - fn->agg_star, - fn->agg_distinct, - fn->func_variadic, - fn->over, - false, - fn->location); + result = ParseFuncOrColumn(pstate, + fn->funcname, + targs, + fn->agg_order, + fn->agg_star, + fn->agg_distinct, + fn->func_variadic, + fn->over, + false, + fn->location); + + /* + * pg_get_expr() is a system function that exposes the expression + * deparsing functionality in ruleutils.c to users. Very handy, but it was + * later realized that the functions in ruleutils.c don't check the input + * rigorously, assuming it to come from system catalogs and to therefore + * be valid. That makes it easy for a user to crash the backend by passing + * a maliciously crafted string representation of an expression to + * pg_get_expr(). + * + * There's a lot of code in ruleutils.c, so it's not feasible to add + * water-proof input checking after the fact. Even if we did it once, it + * would need to be taken into account in any future patches too. + * + * Instead, we restrict pg_rule_expr() to only allow input from system + * catalogs instead. This is a hack, but it's the most robust and easiest + * to backpatch way of plugging the vulnerability. + * + * This is transparent to the typical usage pattern of + * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo', + * ...)", even if 'foo' is a valid expression fetched earlier from a + * system catalog. Hopefully there's isn't many clients doing that out + * there. + */ + if (result && IsA(result, FuncExpr) &&!superuser()) + { + FuncExpr *fe = (FuncExpr *) result; + + if (fe->funcid == F_PG_GET_EXPR || fe->funcid == F_PG_GET_EXPR_EXT) + { + Expr *arg = linitial(fe->args); + bool allowed = false; + + /* + * Check that the argument came directly from one of the allowed + * system catalog columns + */ + if (IsA(arg, Var)) + { + Var *var = (Var *) arg; + RangeTblEntry *rte; + + rte = GetRTEByRangeTablePosn(pstate, + var->varno, var->varlevelsup); + + switch (rte->relid) + { + case IndexRelationId: + if (var->varattno == Anum_pg_index_indexprs || + var->varattno == Anum_pg_index_indpred) + allowed = true; + break; + + case AttrDefaultRelationId: + if (var->varattno == Anum_pg_attrdef_adbin) + allowed = true; + break; + + case ProcedureRelationId: + if (var->varattno == Anum_pg_proc_proargdefaults) + allowed = true; + break; + + case ConstraintRelationId: + if (var->varattno == Anum_pg_constraint_conbin) + allowed = true; + break; + + case TypeRelationId: + if (var->varattno == Anum_pg_type_typdefaultbin) + allowed = true; + break; + } + } + if (!allowed) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("argument to pg_get_expr() must come from system catalogs"))); + } + } + return result; } static Node * @@ -1251,7 +1455,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) return result; pstate->p_hasSubLinks = true; - qtree = parse_sub_analyze(sublink->subselect, pstate); + qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false); /* * Check that we got something reasonable. Many of these conditions are @@ -1773,7 +1977,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) XMLOID, "XMLSERIALIZE")); - targetType = typenameTypeId(pstate, xs->typename, &targetTypmod); + targetType = typenameTypeId(pstate, xs->typeName, &targetTypmod); xexpr->xmloption = xs->xmloption; xexpr->location = xs->location; @@ -1853,25 +2057,42 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) &sublevels_up); Assert(sublevels_up == 0); - /* If a parameter is used, it must be of type REFCURSOR */ - if (cexpr->cursor_name == NULL) + /* + * Check to see if the cursor name matches a parameter of type REFCURSOR. + * If so, replace the raw name reference with a parameter reference. (This + * is a hack for the convenience of plpgsql.) + */ + if (cexpr->cursor_name != NULL) /* in case already transformed */ { - Oid *pptype = find_param_type(pstate, cexpr->cursor_param, -1); + ColumnRef *cref = makeNode(ColumnRef); + Node *node = NULL; - if (pstate->p_variableparams && *pptype == UNKNOWNOID) - { - /* resolve unknown param type as REFCURSOR */ - *pptype = REFCURSOROID; - } - else if (*pptype != REFCURSOROID) + /* Build an unqualified ColumnRef with the given name */ + cref->fields = list_make1(makeString(cexpr->cursor_name)); + cref->location = -1; + + /* See if there is a translation available from a parser hook */ + if (pstate->p_pre_columnref_hook != NULL) + node = (*pstate->p_pre_columnref_hook) (pstate, cref); + if (node == NULL && pstate->p_post_columnref_hook != NULL) + node = (*pstate->p_post_columnref_hook) (pstate, cref, NULL); + + /* + * XXX Should we throw an error if we get a translation that isn't a + * refcursor Param? For now it seems best to silently ignore false + * matches. + */ + if (node != NULL && IsA(node, Param)) { - 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(REFCURSOROID)))); + Param *p = (Param *) node; + + if (p->paramkind == PARAM_EXTERN && + p->paramtype == REFCURSOROID) + { + /* Matches, so convert CURRENT OF to a param reference */ + cexpr->cursor_name = NULL; + cexpr->cursor_param = p->paramid; + } } } @@ -1888,23 +2109,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, creating it if needed */ - - rte = refnameRangeTblEntry(pstate, schemaname, relname, location, - &sublevels_up); - - if (rte == NULL) - rte = addImplicitRTE(pstate, - makeRangeVar(schemaname, relname, location)); + /* Find the RTE's rangetable location */ vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); @@ -2000,7 +2212,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) int32 targetTypmod; int location; - targetType = typenameTypeId(pstate, tc->typename, &targetTypmod); + targetType = typenameTypeId(pstate, tc->typeName, &targetTypmod); if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ @@ -2012,7 +2224,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) */ location = tc->location; if (location < 0) - location = tc->typename->location; + location = tc->typeName->location; result = coerce_to_target_type(pstate, expr, inputType, targetType, targetTypmod, |