From 27a4f06adee023d9172abaf128e35e3cc068108a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 2 Apr 2004 19:07:02 +0000 Subject: [PATCH] Get rid of crocky use of RangeVar nodes in parser to represent partially transformed whole-row variables. Cleaner to use regular whole-row Vars. --- src/backend/optimizer/util/clauses.c | 22 +-- src/backend/parser/parse_expr.c | 174 +++++++++++------ src/backend/parser/parse_func.c | 274 +++++++++------------------ src/backend/parser/parse_relation.c | 24 ++- src/backend/parser/parse_target.c | 21 +- src/include/parser/parse_relation.h | 7 +- 6 files changed, 228 insertions(+), 294 deletions(-) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index b05f760ade..6aea0811bb 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.167 2004/03/24 22:40:28 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2550,16 +2550,6 @@ expression_tree_walker(Node *node, return true; } break; - case T_RangeVar: - /* - * Give a useful complaint if someone uses a bare relation name - * in an expression (see comments in transformColumnRef()). - */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("relation reference \"%s\" cannot be used in an expression", - ((RangeVar *) node)->relname))); - break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); @@ -3031,16 +3021,6 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_RangeVar: - /* - * Give a useful complaint if someone uses a bare relation name - * in an expression (see comments in transformColumnRef()). - */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("relation reference \"%s\" cannot be used in an expression", - ((RangeVar *) node)->relname))); - break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 83daae9b62..acee2b300e 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.167 2004/03/24 22:40:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.168 2004/04/02 19:06:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,8 @@ bool Transform_null_equals = false; static Node *typecast_expression(ParseState *pstate, Node *expr, TypeName *typename); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); +static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, + char *relname); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); @@ -932,34 +934,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) { int numnames = length(cref->fields); Node *node; - RangeVar *rv; int levels_up; /*---------- * The allowed syntaxes are: * * A First try to resolve as unqualified column name; - * if no luck, try to resolve as unqual. table name (A.*). - * A.B A is an unqual. table name; B is either a + * if no luck, try to resolve as unqualified table name (A.*). + * A.B A is an unqualified table name; B is either a * column or function name (trying column name first). * A.B.C schema A, table B, col or func name C. * A.B.C.D catalog A, schema B, table C, col or func D. - * A.* A is an unqual. table name; means whole-row value. + * A.* A is an unqualified table name; means whole-row value. * A.B.* whole-row value of table B in schema A. * A.B.C.* whole-row value of table C in schema B in catalog A. * * We do not need to cope with bare "*"; that will only be accepted by * the grammar at the top level of a SELECT list, and transformTargetList - * will take care of it before it ever gets here. + * will take care of it before it ever gets here. Also, "A.*" etc will + * be expanded by transformTargetList if they appear at SELECT top level, + * so here we are only going to see them as function or operator inputs. * * Currently, if a catalog name is given then it must equal the current * database name; we check it here and then discard it. - * - * For whole-row references, the result is an untransformed RangeVar, - * which will work as the argument to a function call, but not in any - * other context at present. (We could instead coerce to a whole-row Var, - * but that will fail for subselect and join RTEs, because there is no - * pg_type entry for their rowtypes.) *---------- */ switch (numnames) @@ -1001,16 +998,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) if (cref->indirection == NIL && refnameRangeTblEntry(pstate, NULL, name, &levels_up) != NULL) - { - rv = makeNode(RangeVar); - rv->relname = name; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; - } + node = transformWholeRowRef(pstate, NULL, name); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" does not exist", name))); + errmsg("column \"%s\" does not exist", + name))); } break; } @@ -1022,10 +1015,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (strcmp(name2, "*") == 0) { - rv = makeNode(RangeVar); - rv->relname = name1; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; + node = transformWholeRowRef(pstate, NULL, name1); break; } @@ -1038,12 +1028,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * try it as a function call. Here, we will create an * implicit RTE for tables not already entered. */ - rv = makeNode(RangeVar); - rv->relname = name1; - rv->inhOpt = INH_DEFAULT; + node = transformWholeRowRef(pstate, NULL, name1); node = ParseFuncOrColumn(pstate, makeList1(makeString(name2)), - makeList1(rv), + makeList1(node), false, false, true); } break; @@ -1057,11 +1045,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (strcmp(name3, "*") == 0) { - rv = makeNode(RangeVar); - rv->schemaname = name1; - rv->relname = name2; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; + node = transformWholeRowRef(pstate, name1, name2); break; } @@ -1070,13 +1054,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) if (node == NULL) { /* Try it as a function call */ - rv = makeNode(RangeVar); - rv->schemaname = name1; - rv->relname = name2; - rv->inhOpt = INH_DEFAULT; + node = transformWholeRowRef(pstate, name1, name2); node = ParseFuncOrColumn(pstate, makeList1(makeString(name3)), - makeList1(rv), + makeList1(node), false, false, true); } break; @@ -1100,11 +1081,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* Whole-row reference? */ if (strcmp(name4, "*") == 0) { - rv = makeNode(RangeVar); - rv->schemaname = name2; - rv->relname = name3; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; + node = transformWholeRowRef(pstate, name2, name3); break; } @@ -1113,13 +1090,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) if (node == NULL) { /* Try it as a function call */ - rv = makeNode(RangeVar); - rv->schemaname = name2; - rv->relname = name3; - rv->inhOpt = INH_DEFAULT; + node = transformWholeRowRef(pstate, name2, name3); node = ParseFuncOrColumn(pstate, makeList1(makeString(name4)), - makeList1(rv), + makeList1(node), false, false, true); } break; @@ -1136,6 +1110,99 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) return transformIndirection(pstate, node, cref->indirection); } +/* + * Construct a whole-row reference to represent the notation "relation.*". + * + * In simple cases, this will be a Var with varno set to the correct range + * table entry, and varattno == 0 to signal that it references the whole + * tuple. (Use of zero here is unclean, since it could easily be confused + * with error cases, but it's not worth changing now.) The vartype indicates + * a rowtype; either a named composite type, or RECORD. + * + * We also need the ability to build a row-constructor expression, but the + * infrastructure for that doesn't exist just yet. + */ +static Node * +transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname) +{ + Node *result; + RangeTblEntry *rte; + int vnum; + int sublevels_up; + Oid toid; + + /* Look up the referenced RTE, creating it if needed */ + + rte = refnameRangeTblEntry(pstate, schemaname, relname, + &sublevels_up); + + if (rte == NULL) + rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname)); + + vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); + + /* Build the appropriate referencing node */ + + switch (rte->rtekind) + { + case RTE_RELATION: + /* relation: the rowtype is a named composite type */ + toid = get_rel_type_id(rte->relid); + if (!OidIsValid(toid)) + elog(ERROR, "could not find type OID for relation %u", + rte->relid); + result = (Node *) makeVar(vnum, + InvalidAttrNumber, + toid, + -1, + sublevels_up); + break; + case RTE_FUNCTION: + toid = exprType(rte->funcexpr); + if (toid == RECORDOID || get_typtype(toid) == 'c') + { + /* func returns composite; same as relation case */ + result = (Node *) makeVar(vnum, + InvalidAttrNumber, + toid, + -1, + sublevels_up); + } + else + { + /* + * func returns scalar; instead of making a whole-row Var, + * just reference the function's scalar output. (XXX this + * seems a tad inconsistent, especially if "f.*" was + * explicitly written ...) + */ + result = (Node *) makeVar(vnum, + 1, + toid, + -1, + sublevels_up); + } + break; + default: + /* + * RTE is a join or subselect. For the moment we represent this + * as a whole-row Var of RECORD type, but this will not actually + * work; need a row-constructor expression instead. + * + * XXX after fixing, be sure that unknown_attribute still + * does the right thing. + */ + result = (Node *) makeVar(vnum, + InvalidAttrNumber, + RECORDOID, + -1, + sublevels_up); + break; + } + + return result; +} + /* * exprType - * returns the Oid of the type of the expression. (Used for typechecking.) @@ -1294,19 +1361,6 @@ exprType(Node *expr) case T_SetToDefault: type = ((SetToDefault *) expr)->typeId; break; - case T_RangeVar: - - /* - * If someone uses a bare relation name in an expression, we - * will likely first notice a problem here (see comments in - * transformColumnRef()). Issue an appropriate error message. - */ - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("relation reference \"%s\" cannot be used in an expression", - ((RangeVar *) expr)->relname))); - type = InvalidOid; /* keep compiler quiet */ - break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); type = InvalidOid; /* keep compiler quiet */ diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 1677493abb..deab6c2ad3 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.167 2004/04/02 19:06:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,14 +32,14 @@ #include "utils/syscache.h" -static Node *ParseComplexProjection(char *funcname, Node *first_arg); +static Node *ParseComplexProjection(ParseState *pstate, char *funcname, + Node *first_arg); static Oid **argtype_inherit(int nargs, Oid *argtypes); static int find_inheritors(Oid relid, Oid **supervec); static Oid **gen_cross_product(InhPaths *arginh, int nargs); static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid); -static void unknown_attribute(const char *schemaname, const char *relname, - const char *attname); +static void unknown_attribute(ParseState *pstate, Node *relref, char *attname); /* @@ -48,7 +48,9 @@ static void unknown_attribute(const char *schemaname, const char *relname, * For historical reasons, Postgres tries to treat the notations tab.col * and col(tab) as equivalent: if a single-argument function call has an * argument of complex type and the (unqualified) function name matches - * any attribute of the type, we take it as a column projection. + * any attribute of the type, we take it as a column projection. Conversely + * a function of a single complex-type argument can be written like a + * column reference, allowing functions to act like computed columns. * * Hence, both cases come through here. The is_column parameter tells us * which syntactic construct is actually being dealt with, but this is @@ -57,9 +59,7 @@ static void unknown_attribute(const char *schemaname, const char *relname, * a single argument (the putative table), unqualified function name * equal to the column name, and no aggregate decoration. * - * In the function-call case, the argument expressions have been transformed - * already. In the column case, we may get either a transformed expression - * or a RangeVar node as argument. + * The argument expressions (in fargs) must have been transformed already. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, @@ -96,44 +96,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, } /* - * check for column projection: if function has one argument, and that + * Check for column projection: if function has one argument, and that * argument is of complex type, and function name is not qualified, * then the "function call" could be a projection. We also check that * there wasn't any aggregate decoration. */ if (nargs == 1 && !agg_star && !agg_distinct && length(funcname) == 1) { - char *cname = strVal(lfirst(funcname)); + Oid argtype = exprType(first_arg); - /* Is it a not-yet-transformed RangeVar node? */ - if (IsA(first_arg, RangeVar)) + if (argtype == RECORDOID || ISCOMPLEX(argtype)) { - /* First arg is a relation. This could be a projection. */ - retval = qualifiedNameToVar(pstate, - ((RangeVar *) first_arg)->schemaname, - ((RangeVar *) first_arg)->relname, - cname, - true); + retval = ParseComplexProjection(pstate, + strVal(lfirst(funcname)), + first_arg); if (retval) return retval; - } - else if (ISCOMPLEX(exprType(first_arg))) - { /* - * Attempt to handle projection of a complex argument. If - * ParseComplexProjection can't handle the projection, we have - * to keep going. + * If ParseComplexProjection doesn't recognize it as a projection, + * just press on. */ - retval = ParseComplexProjection(cname, first_arg); - if (retval) - return retval; } } /* * Okay, it's not a column projection, so it must really be a - * function. Extract arg type info and transform RangeVar arguments - * into varnodes of the appropriate form. + * function. Extract arg type info in preparation for function lookup. */ MemSet(actual_arg_types, 0, FUNC_MAX_ARGS * sizeof(Oid)); @@ -141,96 +129,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, foreach(i, fargs) { Node *arg = lfirst(i); - Oid toid; - if (IsA(arg, RangeVar)) - { - char *schemaname; - char *relname; - RangeTblEntry *rte; - int vnum; - int sublevels_up; - - /* - * a relation: look it up in the range table, or add if needed - */ - schemaname = ((RangeVar *) arg)->schemaname; - relname = ((RangeVar *) arg)->relname; - - rte = refnameRangeTblEntry(pstate, schemaname, relname, - &sublevels_up); - - if (rte == NULL) - rte = addImplicitRTE(pstate, (RangeVar *) arg); - - vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - - /* - * The parameter to be passed to the function is the whole - * tuple from the relation. We build a special VarNode to - * reflect this -- it has varno set to the correct range table - * entry, but has varattno == 0 to signal that the whole tuple - * is the argument. - */ - switch (rte->rtekind) - { - case RTE_RELATION: - toid = get_rel_type_id(rte->relid); - if (!OidIsValid(toid)) - elog(ERROR, "could not find type OID for relation %u", - rte->relid); - /* replace RangeVar in the arg list */ - lfirst(i) = makeVar(vnum, - InvalidAttrNumber, - toid, - -1, - sublevels_up); - break; - case RTE_FUNCTION: - toid = exprType(rte->funcexpr); - if (get_typtype(toid) == 'c') - { - /* func returns composite; same as relation case */ - lfirst(i) = makeVar(vnum, - InvalidAttrNumber, - toid, - -1, - sublevels_up); - } - else - { - /* func returns scalar; use attno 1 instead */ - lfirst(i) = makeVar(vnum, - 1, - toid, - -1, - sublevels_up); - } - break; - default: - - /* - * RTE is a join or subselect; must fail for lack of a - * named tuple type - * - * XXX FIXME - */ - if (is_column) - unknown_attribute(schemaname, relname, - strVal(lfirst(funcname))); - else - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot pass result of subquery or join \"%s\" to a function", - relname))); - toid = InvalidOid; /* keep compiler quiet */ - break; - } - } - else - toid = exprType(arg); - - actual_arg_types[argn++] = toid; + actual_arg_types[argn++] = exprType(arg); } /* @@ -281,25 +181,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, */ if (is_column) { - char *colname = strVal(lfirst(funcname)); - Oid relTypeId; - Assert(nargs == 1); - if (IsA(first_arg, RangeVar)) - unknown_attribute(((RangeVar *) first_arg)->schemaname, - ((RangeVar *) first_arg)->relname, - colname); - relTypeId = exprType(first_arg); - if (!ISCOMPLEX(relTypeId)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("attribute notation .%s applied to type %s, which is not a complex type", - colname, format_type_be(relTypeId)))); - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("attribute \"%s\" not found in data type %s", - colname, format_type_be(relTypeId)))); + Assert(length(funcname) == 1); + unknown_attribute(pstate, first_arg, strVal(lfirst(funcname))); } /* @@ -1284,80 +1168,92 @@ setup_field_select(Node *input, char *attname, Oid relid) * handles function calls with a single argument that is of complex type. * If the function call is actually a column projection, return a suitably * transformed expression tree. If not, return NULL. - * - * NB: argument is expected to be transformed already, ie, not a RangeVar. */ static Node * -ParseComplexProjection(char *funcname, Node *first_arg) +ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg) { - Oid argtype = exprType(first_arg); + Oid argtype; Oid argrelid; AttrNumber attnum; - FieldSelect *fselect; + /* + * Special case for whole-row Vars so that we can resolve (foo.*).bar + * even when foo is a reference to a subselect, join, or RECORD function. + * A bonus is that we avoid generating an unnecessary FieldSelect; our + * result can omit the whole-row Var and just be a Var for the selected + * field. + */ + if (IsA(first_arg, Var) && + ((Var *) first_arg)->varattno == InvalidAttrNumber) + { + RangeTblEntry *rte; + + rte = GetRTEByRangeTablePosn(pstate, + ((Var *) first_arg)->varno, + ((Var *) first_arg)->varlevelsup); + /* Return a Var if funcname matches a column, else NULL */ + return scanRTEForColumn(pstate, rte, funcname); + } + + /* + * Else do it the hard way. Note that if the arg is of RECORD type, + * we will never recognize a column name, and always assume the item + * must be a function. + */ + argtype = exprType(first_arg); argrelid = typeidTypeRelid(argtype); if (!argrelid) - return NULL; /* probably should not happen */ + return NULL; /* can only happen if RECORD */ + attnum = get_attnum(argrelid, funcname); if (attnum == InvalidAttrNumber) return NULL; /* funcname does not match any column */ - /* - * Check for special cases where we don't want to return a - * FieldSelect. - */ - switch (nodeTag(first_arg)) - { - case T_Var: - { - Var *var = (Var *) first_arg; - - /* - * If the Var is a whole-row tuple, we can just replace it - * with a simple Var reference. - */ - if (var->varattno == InvalidAttrNumber) - { - Oid vartype; - int32 vartypmod; - - get_atttypetypmod(argrelid, attnum, - &vartype, &vartypmod); - - return (Node *) makeVar(var->varno, - attnum, - vartype, - vartypmod, - var->varlevelsup); - } - break; - } - default: - break; - } - - /* Else generate a FieldSelect expression */ - fselect = setup_field_select(first_arg, funcname, argrelid); - return (Node *) fselect; + /* Success, so generate a FieldSelect expression */ + return (Node *) setup_field_select(first_arg, funcname, argrelid); } /* - * Simple helper routine for delivering "column does not exist" error message + * helper routine for delivering "column does not exist" error message */ static void -unknown_attribute(const char *schemaname, const char *relname, - const char *attname) +unknown_attribute(ParseState *pstate, Node *relref, char *attname) { - if (schemaname) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column %s.%s.%s does not exist", - schemaname, relname, attname))); - else + RangeTblEntry *rte; + + if (IsA(relref, Var)) + { + /* 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", - relname, attname))); + rte->eref->aliasname, attname))); + } + 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)))); + else if (relTypeId == RECORDOID) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("could not identify column \"%s\" in record data type", + attname))); + 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)))); + } } /* diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 3e314bea96..4df92ee310 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.93 2004/04/02 19:06:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid); static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, RangeTblEntry *rte1, const char *aliasname1); -static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, - char *colname); static bool isForUpdate(ParseState *pstate, char *refname); static bool get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum); @@ -424,6 +422,24 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) return 0; /* keep compiler quiet */ } +/* + * Given an RT index and nesting depth, find the corresponding RTE. + * This is the inverse of RTERangeTablePosn. + */ +RangeTblEntry * +GetRTEByRangeTablePosn(ParseState *pstate, + int varno, + int sublevels_up) +{ + while (sublevels_up-- > 0) + { + pstate = pstate->parentParseState; + Assert(pstate != NULL); + } + Assert(varno > 0 && varno <= length(pstate->p_rtable)); + return rt_fetch(varno, pstate->p_rtable); +} + /* * scanRTEForColumn * Search the column names of a single RTE for the given name. @@ -439,7 +455,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) * expression can only appear in a FROM clause, and any table named in * FROM will be marked as requiring read access from the beginning. */ -static Node * +Node * scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) { Node *result = NULL; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6bed23f906..c9c44ac238 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.115 2004/02/13 01:08:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,14 +60,6 @@ transformTargetEntry(ParseState *pstate, if (expr == NULL) expr = transformExpr(pstate, node); - if (IsA(expr, RangeVar)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("relation reference \"%s\" cannot be used as a select-list entry", - ((RangeVar *) expr)->relname), - errhint("Write \"%s\".* to denote all the columns of the relation.", - ((RangeVar *) expr)->relname))); - type_id = exprType(expr); type_mod = exprTypmod(expr); @@ -243,21 +235,12 @@ markTargetListOrigins(ParseState *pstate, List *targetlist) static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var) { - Index levelsup; RangeTblEntry *rte; AttrNumber attnum; if (var == NULL || !IsA(var, Var)) return; - levelsup = var->varlevelsup; - while (levelsup-- > 0) - { - pstate = pstate->parentParseState; - Assert(pstate != NULL); - } - Assert(var->varno > 0 && - (int) var->varno <= length(pstate->p_rtable)); - rte = rt_fetch(var->varno, pstate->p_rtable); + rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup); attnum = var->varattno; switch (rte->rtekind) diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index ca209c9302..0fa75d0297 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.42 2003/11/29 22:41:09 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.43 2004/04/02 19:07:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,11 @@ extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, extern int RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up); +extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate, + int varno, + int sublevels_up); +extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, + char *colname); extern Node *colnameToVar(ParseState *pstate, char *colname); extern Node *qualifiedNameToVar(ParseState *pstate, char *schemaname,