diff options
Diffstat (limited to 'src/backend/parser')
| -rw-r--r-- | src/backend/parser/analyze.c | 44 | ||||
| -rw-r--r-- | src/backend/parser/parse_clause.c | 162 | ||||
| -rw-r--r-- | src/backend/parser/parse_expr.c | 4 | ||||
| -rw-r--r-- | src/backend/parser/parse_func.c | 55 | ||||
| -rw-r--r-- | src/backend/parser/parse_node.c | 53 | ||||
| -rw-r--r-- | src/backend/parser/parse_relation.c | 357 | ||||
| -rw-r--r-- | src/backend/parser/parse_target.c | 66 |
7 files changed, 386 insertions, 355 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 5fb298e1c2..98f5030f78 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.219 2002/03/10 06:02:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.220 2002/03/12 00:51:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2045,10 +2045,12 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) Node *node; List *lefttl, *dtlist, - *targetvars, + *colMods, *targetnames, - *sv_namespace; - JoinExpr *jnode; + *sv_namespace, + *sv_rtable; + RangeTblEntry *jrte; + RangeTblRef *jrtr; int tllen; qry->commandType = CMD_SELECT; @@ -2115,12 +2117,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * Generate dummy targetlist for outer query using column names of * leftmost select and common datatypes of topmost set operation. Also - * make lists of the dummy vars and their names for use in parsing - * ORDER BY. + * make a list of the column names for use in parsing ORDER BY. + * + * XXX colMods is a hack to provide a dummy typmod list below. We + * should probably keep track of common typmod instead. */ qry->targetList = NIL; - targetvars = NIL; targetnames = NIL; + colMods = NIL; lefttl = leftmostQuery->targetList; foreach(dtlist, sostmt->colTypes) { @@ -2135,15 +2139,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) -1, colName, false); - expr = (Node *) makeVar(leftmostRTI, + expr = (Node *) makeVar(1, leftResdom->resno, colType, -1, 0); qry->targetList = lappend(qry->targetList, makeTargetEntry(resdom, expr)); - targetvars = lappend(targetvars, expr); targetnames = lappend(targetnames, makeString(colName)); + colMods = lappendi(colMods, -1); lefttl = lnext(lefttl); } @@ -2190,7 +2194,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * As a first step towards supporting sort clauses that are * expressions using the output columns, generate a namespace entry - * that makes the output columns visible. A JoinExpr node is handy + * that makes the output columns visible. A Join RTE node is handy * for this, since we can easily control the Vars generated upon * matches. * @@ -2198,12 +2202,23 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * "ORDER BY upper(foo)" will draw the right error message rather than * "foo not found". */ - jnode = makeNode(JoinExpr); - jnode->colnames = targetnames; - jnode->colvars = targetvars; + jrte = addRangeTableEntryForJoin(NULL, + targetnames, + JOIN_INNER, + sostmt->colTypes, + colMods, + NIL, + NIL, + NULL, + true); + jrtr = makeNode(RangeTblRef); + jrtr->rtindex = 1; + + sv_rtable = pstate->p_rtable; + pstate->p_rtable = makeList1(jrte); sv_namespace = pstate->p_namespace; - pstate->p_namespace = makeList1(jnode); + pstate->p_namespace = makeList1(jrtr); /* * For now, we don't support resjunk sort clauses on the output of a @@ -2218,6 +2233,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->targetList); pstate->p_namespace = sv_namespace; + pstate->p_rtable = sv_rtable; if (tllen != length(qry->targetList)) elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"); diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 61904439e7..2f1eda4a9b 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.83 2001/10/25 05:49:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.84 2002/03/12 00:51:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -503,7 +503,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) *res_colnames, *l_colvars, *r_colvars, - *res_colvars; + *coltypes, + *coltypmods, + *leftcolnos, + *rightcolnos; + Index leftrti, + rightrti; + RangeTblEntry *rte; /* * Recursively process the left and right subtrees @@ -525,39 +531,32 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) /* * Extract column name and var lists from both subtrees + * + * Note: expandRTE returns new lists, safe for me to modify */ - if (IsA(j->larg, JoinExpr)) - { - /* Make a copy of the subtree's lists so we can modify! */ - l_colnames = copyObject(((JoinExpr *) j->larg)->colnames); - l_colvars = copyObject(((JoinExpr *) j->larg)->colvars); - } + if (IsA(j->larg, RangeTblRef)) + leftrti = ((RangeTblRef *) j->larg)->rtindex; + else if (IsA(j->larg, JoinExpr)) + leftrti = ((JoinExpr *) j->larg)->rtindex; else { - RangeTblEntry *rte; - - Assert(IsA(j->larg, RangeTblRef)); - rte = rt_fetch(((RangeTblRef *) j->larg)->rtindex, - pstate->p_rtable); - expandRTE(pstate, rte, &l_colnames, &l_colvars); - /* expandRTE returns new lists, so no need for copyObject */ - } - if (IsA(j->rarg, JoinExpr)) - { - /* Make a copy of the subtree's lists so we can modify! */ - r_colnames = copyObject(((JoinExpr *) j->rarg)->colnames); - r_colvars = copyObject(((JoinExpr *) j->rarg)->colvars); + elog(ERROR, "transformFromClauseItem: unexpected subtree type"); + leftrti = 0; /* keep compiler quiet */ } + rte = rt_fetch(leftrti, pstate->p_rtable); + expandRTE(pstate, rte, &l_colnames, &l_colvars); + + if (IsA(j->rarg, RangeTblRef)) + rightrti = ((RangeTblRef *) j->rarg)->rtindex; + else if (IsA(j->rarg, JoinExpr)) + rightrti = ((JoinExpr *) j->rarg)->rtindex; else { - RangeTblEntry *rte; - - Assert(IsA(j->rarg, RangeTblRef)); - rte = rt_fetch(((RangeTblRef *) j->rarg)->rtindex, - pstate->p_rtable); - expandRTE(pstate, rte, &r_colnames, &r_colvars); - /* expandRTE returns new lists, so no need for copyObject */ + elog(ERROR, "transformFromClauseItem: unexpected subtree type"); + rightrti = 0; /* keep compiler quiet */ } + rte = rt_fetch(rightrti, pstate->p_rtable); + expandRTE(pstate, rte, &r_colnames, &r_colvars); /* * Natural join does not explicitly specify columns; must generate @@ -604,7 +603,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) * Now transform the join qualifications, if any. */ res_colnames = NIL; - res_colvars = NIL; + coltypes = NIL; + coltypmods = NIL; + leftcolnos = NIL; + rightcolnos = NIL; if (j->using) { @@ -624,9 +626,10 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) { char *u_colname = strVal(lfirst(ucol)); List *col; - Node *l_colvar, - *r_colvar, - *colvar; + Var *l_colvar, + *r_colvar; + Oid outcoltype; + int32 outcoltypmod; int ndx; int l_index = -1; int r_index = -1; @@ -672,34 +675,28 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) res_colnames = lappend(res_colnames, nth(l_index, l_colnames)); - switch (j->jointype) + /* + * Choose output type if input types are dissimilar. + */ + outcoltype = l_colvar->vartype; + outcoltypmod = l_colvar->vartypmod; + if (outcoltype != r_colvar->vartype) { - case JOIN_INNER: - case JOIN_LEFT: - colvar = l_colvar; - break; - case JOIN_RIGHT: - colvar = r_colvar; - break; - default: - { - /* Need COALESCE(l_colvar, r_colvar) */ - CaseExpr *c = makeNode(CaseExpr); - CaseWhen *w = makeNode(CaseWhen); - NullTest *n = makeNode(NullTest); - - n->arg = l_colvar; - n->nulltesttype = IS_NOT_NULL; - w->expr = (Node *) n; - w->result = l_colvar; - c->args = makeList1(w); - c->defresult = r_colvar; - colvar = transformExpr(pstate, (Node *) c, - EXPR_COLUMN_FIRST); - break; - } + outcoltype = + select_common_type(makeListi2(l_colvar->vartype, + r_colvar->vartype), + "JOIN/USING"); + outcoltypmod = -1; /* ie, unknown */ } - res_colvars = lappend(res_colvars, colvar); + else if (outcoltypmod != r_colvar->vartypmod) + { + /* same type, but not same typmod */ + outcoltypmod = -1; /* ie, unknown */ + } + coltypes = lappendi(coltypes, outcoltype); + coltypmods = lappendi(coltypmods, outcoltypmod); + leftcolnos = lappendi(leftcolnos, l_index+1); + rightcolnos = lappendi(rightcolnos, r_index+1); } j->quals = transformJoinUsingClause(pstate, @@ -724,30 +721,53 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels) r_colnames, r_colvars, &r_colnames, &r_colvars); res_colnames = nconc(res_colnames, l_colnames); - res_colvars = nconc(res_colvars, l_colvars); + while (l_colvars) + { + Var *l_var = (Var *) lfirst(l_colvars); + + coltypes = lappendi(coltypes, l_var->vartype); + coltypmods = lappendi(coltypmods, l_var->vartypmod); + leftcolnos = lappendi(leftcolnos, l_var->varattno); + rightcolnos = lappendi(rightcolnos, 0); + l_colvars = lnext(l_colvars); + } res_colnames = nconc(res_colnames, r_colnames); - res_colvars = nconc(res_colvars, r_colvars); + while (r_colvars) + { + Var *r_var = (Var *) lfirst(r_colvars); + + coltypes = lappendi(coltypes, r_var->vartype); + coltypmods = lappendi(coltypmods, r_var->vartypmod); + leftcolnos = lappendi(leftcolnos, 0); + rightcolnos = lappendi(rightcolnos, r_var->varattno); + r_colvars = lnext(r_colvars); + } /* - * Process alias (AS clause), if any. + * Check alias (AS clause), if any. */ if (j->alias) { - /* - * If a column alias list is specified, substitute the alias - * names into my output-column list - */ if (j->alias->attrs != NIL) { - if (length(j->alias->attrs) != length(res_colnames)) - elog(ERROR, "Column alias list for \"%s\" has wrong number of entries (need %d)", - j->alias->relname, length(res_colnames)); - res_colnames = j->alias->attrs; + if (length(j->alias->attrs) > length(res_colnames)) + elog(ERROR, "Column alias list for \"%s\" has too many entries", + j->alias->relname); } } - j->colnames = res_colnames; - j->colvars = res_colvars; + /* + * Now build an RTE for the result of the join + */ + rte = addRangeTableEntryForJoin(pstate, res_colnames, + j->jointype, + coltypes, coltypmods, + leftcolnos, rightcolnos, + j->alias, true); + + /* assume new rte is at end */ + j->rtindex = length(pstate->p_rtable); + Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable)); return (Node *) j; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index f740d632cc..9c32fac231 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.107 2002/03/07 16:35:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.108 2002/03/12 00:51:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -646,7 +646,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence) * appear */ if (ident->indirection == NIL && - refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL) + refnameRangeTblEntry(pstate, ident->name, &sublevels_up) != NULL) { ident->isRel = TRUE; result = (Node *) ident; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 766a5daad5..ed39d6c103 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.116 2002/02/19 20:11:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -316,7 +316,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, { RangeTblEntry *rte; int vnum; - Node *rteorjoin; int sublevels_up; /* @@ -324,49 +323,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, */ refname = ((Ident *) arg)->name; - rteorjoin = refnameRangeOrJoinEntry(pstate, refname, - &sublevels_up); + rte = refnameRangeTblEntry(pstate, refname, + &sublevels_up); - if (rteorjoin == NULL) + if (rte == NULL) rte = addImplicitRTE(pstate, refname); - else if (IsA(rteorjoin, RangeTblEntry)) - rte = (RangeTblEntry *) rteorjoin; - else if (IsA(rteorjoin, JoinExpr)) - { - /* - * The relation name refers to a join. We can't support - * functions on join tuples (since we don't have a named - * type for the join tuples), so error out. - */ - if (nargs == 1) - { - /* - * We have f(x) or more likely x.f where x is a join - * and f is not one of the attribute names of the join - * (else we'd have recognized it above). Give an - * appropriately vague error message. Would be nicer - * to know which syntax was used... - */ - elog(ERROR, "No such attribute or function %s.%s", - refname, funcname); - } - else - { - /* - * There are multiple arguments, so it must be a - * function call. - */ - elog(ERROR, "Cannot pass result of join %s to a function", - refname); - } - rte = NULL; /* keep compiler quiet */ - } - else - { - elog(ERROR, "ParseFuncOrColumn: unexpected node type %d", - nodeTag(rteorjoin)); - rte = NULL; /* keep compiler quiet */ - } vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); @@ -379,11 +340,11 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, * sizeof(Pointer) to signal that the runtime representation * will be a pointer not an Oid. */ - if (rte->relname == NULL) + if (rte->rtekind != RTE_RELATION) { /* - * RTE is a subselect; must fail for lack of a specific - * type + * RTE is a join or subselect; must fail for lack of a + * named tuple type */ if (nargs == 1) { @@ -397,7 +358,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, } else { - elog(ERROR, "Cannot pass result of sub-select %s to a function", + elog(ERROR, "Cannot pass result of sub-select or join %s to a function", refname); } } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index a43dcb13af..be825c26f9 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,21 +8,22 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.58 2002/03/06 06:09:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.59 2002/03/12 00:51:55 tgl Exp $ * *------------------------------------------------------------------------- */ +#include "postgres.h" + #include <ctype.h> #include <errno.h> #include <float.h> -#include "postgres.h" - #include "access/heapam.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "fmgr.h" #include "nodes/makefuncs.h" +#include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_node.h" @@ -165,51 +166,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno) { int vnum, sublevels_up; - Oid vartypeid = 0; - int32 type_mod = 0; + Oid vartypeid; + int32 type_mod; vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - - if (rte->relid != InvalidOid) - { - /* Plain relation RTE --- get the attribute's type info */ - HeapTuple tp; - Form_pg_attribute att_tup; - - tp = SearchSysCache(ATTNUM, - ObjectIdGetDatum(rte->relid), - Int16GetDatum(attrno), - 0, 0); - /* this shouldn't happen... */ - if (!HeapTupleIsValid(tp)) - elog(ERROR, "Relation %s does not have attribute %d", - rte->relname, attrno); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - vartypeid = att_tup->atttypid; - type_mod = att_tup->atttypmod; - ReleaseSysCache(tp); - } - else - { - /* Subselect RTE --- get type info from subselect's tlist */ - List *tlistitem; - - foreach(tlistitem, rte->subquery->targetList) - { - TargetEntry *te = (TargetEntry *) lfirst(tlistitem); - - if (te->resdom->resjunk || te->resdom->resno != attrno) - continue; - vartypeid = te->resdom->restype; - type_mod = te->resdom->restypmod; - break; - } - /* falling off end of list shouldn't happen... */ - if (tlistitem == NIL) - elog(ERROR, "Subquery %s does not have attribute %d", - rte->eref->relname, attrno); - } - + get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod); return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 9440914a77..1609c89ce0 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.62 2002/03/06 06:09:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,38 +36,41 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, char *refname); static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname); -static Node *scanJoinForColumn(JoinExpr *join, char *colname, - int sublevels_up); static bool isForUpdate(ParseState *pstate, char *relname); -static List *expandNamesVars(ParseState *pstate, List *names, List *vars); static int specialAttNum(char *a); static void warnAutoRange(ParseState *pstate, char *refname); /* - * refnameRangeOrJoinEntry - * Given a refname, look to see if it matches any RTE or join table. - * If so, return a pointer to the RangeTblEntry or JoinExpr. + * refnameRangeTblEntry + * Given a refname, look to see if it matches any RTE. + * If so, return a pointer to the RangeTblEntry. * Optionally get its nesting depth (0 = current). If sublevels_up * is NULL, only consider items at the current nesting level. */ -Node * -refnameRangeOrJoinEntry(ParseState *pstate, - char *refname, - int *sublevels_up) +RangeTblEntry * +refnameRangeTblEntry(ParseState *pstate, + char *refname, + int *sublevels_up) { if (sublevels_up) *sublevels_up = 0; while (pstate != NULL) { - Node *rte; + Node *nsnode; - rte = scanNameSpaceForRefname(pstate, - (Node *) pstate->p_namespace, - refname); - if (rte) - return rte; + nsnode = scanNameSpaceForRefname(pstate, + (Node *) pstate->p_namespace, + refname); + if (nsnode) + { + /* should get an RTE or JoinExpr */ + if (IsA(nsnode, RangeTblEntry)) + return (RangeTblEntry *) nsnode; + Assert(IsA(nsnode, JoinExpr)); + return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable); + } pstate = pstate->parentParseState; if (sublevels_up) @@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) * * Side effect: if we find a match, mark the RTE as requiring read access. * See comments in setTargetTable(). + * + * NOTE: if the RTE is for a join, marking it as requiring read access does + * nothing. It might seem that we need to propagate the mark to all the + * contained RTEs, but that is not necessary. This is so because a join + * expression can only appear in a FROM clause, and any table named in + * FROM will be marked checkForRead from the beginning. */ static Node * scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) @@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) return result; /* - * If the RTE represents a table (not a sub-select), consider system - * column names. + * If the RTE represents a real table, consider system column names. */ - if (rte->relid != InvalidOid) + if (rte->rtekind == RTE_RELATION) { /* quick check to see if name could be a system column */ attnum = specialAttNum(colname); @@ -304,44 +312,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) } /* - * scanJoinForColumn - * Search the column names of a single join table for the given name. - * If found, return an appropriate Var node or expression, else return NULL. - * If the name proves ambiguous within this jointable, raise error. - * - * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing - * checkForRead true for the referenced tables. This is so because a join - * expression can only appear in a FROM clause, and any table named in - * FROM will be marked checkForRead from the beginning. - */ -static Node * -scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up) -{ - Node *result = NULL; - int attnum = 0; - List *c; - - foreach(c, join->colnames) - { - attnum++; - if (strcmp(strVal(lfirst(c)), colname) == 0) - { - if (result) - elog(ERROR, "Column reference \"%s\" is ambiguous", colname); - result = copyObject(nth(attnum - 1, join->colvars)); - - /* - * If referencing an uplevel join item, we must adjust - * sublevels settings in the copied expression. - */ - if (sublevels_up > 0) - IncrementVarSublevelsUp(result, sublevels_up, 0); - } - } - return result; -} - -/* * colnameToVar * Search for an unqualified column name. * If found, return the appropriate Var node (or expression). @@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname) } else if (IsA(nsnode, JoinExpr)) { - JoinExpr *j = (JoinExpr *) nsnode; + int varno = ((JoinExpr *) nsnode)->rtindex; + RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); - newresult = scanJoinForColumn(j, colname, levels_up); + /* joins are always inFromCl, so no need to check */ + + /* use orig_pstate here to get the right sublevels_up */ + newresult = scanRTEForColumn(orig_pstate, rte, colname); } else elog(ERROR, "colnameToVar: unexpected node type %d", @@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname) /* * qualifiedNameToVar * Search for a qualified column name (refname + column name). - * If found, return the appropriate Var node (or expression). + * If found, return the appropriate Var node. * If not found, return NULL. If the name proves ambiguous, raise error. */ Node * qualifiedNameToVar(ParseState *pstate, char *refname, char *colname, bool implicitRTEOK) { - Node *result; - Node *rteorjoin; + RangeTblEntry *rte; int sublevels_up; - rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up); + rte = refnameRangeTblEntry(pstate, refname, &sublevels_up); - if (rteorjoin == NULL) + if (rte == NULL) { if (!implicitRTEOK) return NULL; - rteorjoin = (Node *) addImplicitRTE(pstate, refname); - sublevels_up = 0; + rte = addImplicitRTE(pstate, refname); } - if (IsA(rteorjoin, RangeTblEntry)) - result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin, - colname); - else if (IsA(rteorjoin, JoinExpr)) - result = scanJoinForColumn((JoinExpr *) rteorjoin, - colname, sublevels_up); - else - { - elog(ERROR, "qualifiedNameToVar: unexpected node type %d", - nodeTag(rteorjoin)); - result = NULL; /* keep compiler quiet */ - } - - return result; + return scanRTEForColumn(pstate, rte, colname); } /* @@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate, int numaliases; int varattno; + rte->rtekind = RTE_RELATION; rte->relname = relname; rte->alias = alias; - rte->subquery = NULL; /* * Get the rel's OID. This access also ensures that we have an @@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate, int varattno; List *tlistitem; + rte->rtekind = RTE_SUBQUERY; rte->relname = NULL; rte->relid = InvalidOid; rte->subquery = subquery; @@ -622,6 +582,76 @@ addRangeTableEntryForSubquery(ParseState *pstate, } /* + * Add an entry for a join to the pstate's range table (p_rtable). + * + * This is much like addRangeTableEntry() except that it makes a join RTE. + */ +RangeTblEntry * +addRangeTableEntryForJoin(ParseState *pstate, + List *colnames, + JoinType jointype, + List *coltypes, + List *coltypmods, + List *leftcols, + List *rightcols, + Attr *alias, + bool inFromCl) +{ + RangeTblEntry *rte = makeNode(RangeTblEntry); + Attr *eref; + int numaliases; + + rte->rtekind = RTE_JOIN; + rte->relname = NULL; + rte->relid = InvalidOid; + rte->subquery = NULL; + rte->jointype = jointype; + rte->joincoltypes = coltypes; + rte->joincoltypmods = coltypmods; + rte->joinleftcols = leftcols; + rte->joinrightcols = rightcols; + rte->alias = alias; + + eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL); + numaliases = length(eref->attrs); + + /* fill in any unspecified alias columns */ + if (numaliases < length(colnames)) + { + while (numaliases-- > 0) + colnames = lnext(colnames); + eref->attrs = nconc(eref->attrs, colnames); + } + + rte->eref = eref; + + /*---------- + * Flags: + * - this RTE should be expanded to include descendant tables, + * - this RTE is in the FROM clause, + * - this RTE should be checked for read/write access rights. + * + * Joins are never checked for access rights. + *---------- + */ + rte->inh = false; /* never true for joins */ + rte->inFromCl = inFromCl; + rte->checkForRead = false; + rte->checkForWrite = false; + + rte->checkAsUser = InvalidOid; + + /* + * Add completed RTE to pstate's range table list, but not to join + * list nor namespace --- caller must do that if appropriate. + */ + if (pstate != NULL) + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + +/* * Has the specified relname been selected FOR UPDATE? */ static bool @@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, /* Need the RT index of the entry for creating Vars */ rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); - if (rte->relname) + if (rte->rtekind == RTE_RELATION) { /* Ordinary relation RTE */ Relation rel; int maxattrs; + int numaliases; rel = heap_openr(rte->relname, AccessShareLock); - maxattrs = RelationGetNumberOfAttributes(rel); + numaliases = length(rte->eref->attrs); for (varattno = 0; varattno < maxattrs; varattno++) { @@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, { char *label; - if (varattno < length(rte->eref->attrs)) + if (varattno < numaliases) label = strVal(nth(varattno, rte->eref->attrs)); else label = NameStr(attr->attname); @@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, heap_close(rel, AccessShareLock); } - else + else if (rte->rtekind == RTE_SUBQUERY) { /* Subquery RTE */ List *aliasp = rte->eref->attrs; @@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte, } } } -} + else if (rte->rtekind == RTE_JOIN) + { + /* Join RTE */ + List *aliasp = rte->eref->attrs; + List *coltypes = rte->joincoltypes; + List *coltypmods = rte->joincoltypmods; -/* - * expandRelAttrs - - * makes a list of TargetEntry nodes for the attributes of the rel - */ -List * -expandRelAttrs(ParseState *pstate, RangeTblEntry *rte) -{ - List *name_list, - *var_list; + varattno = 0; + while (aliasp) + { + Assert(coltypes && coltypmods); + varattno++; - expandRTE(pstate, rte, &name_list, &var_list); + if (colnames) + { + char *label = strVal(lfirst(aliasp)); - return expandNamesVars(pstate, name_list, var_list); -} + *colnames = lappend(*colnames, makeString(pstrdup(label))); + } -/* - * expandJoinAttrs - - * makes a list of TargetEntry nodes for the attributes of the join - */ -List * -expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up) -{ - List *vars; + if (colvars) + { + Var *varnode; - vars = copyObject(join->colvars); + varnode = makeVar(rtindex, varattno, + (Oid) lfirsti(coltypes), + (int32) lfirsti(coltypmods), + sublevels_up); - /* - * If referencing an uplevel join item, we must adjust sublevels - * settings in the copied expression. - */ - if (sublevels_up > 0) - IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0); + *colvars = lappend(*colvars, varnode); + } - return expandNamesVars(pstate, - copyObject(join->colnames), - vars); + aliasp = lnext(aliasp); + coltypes = lnext(coltypes); + coltypmods = lnext(coltypmods); + } + Assert(coltypes == NIL && coltypmods == NIL); + } + else + elog(ERROR, "expandRTE: unsupported RTE kind %d", + (int) rte->rtekind); } /* - * expandNamesVars - - * Workhorse for "*" expansion: produce a list of targetentries - * given lists of column names (as String nodes) and var references. + * expandRelAttrs - + * Workhorse for "*" expansion: produce a list of targetentries + * for the attributes of the rte */ -static List * -expandNamesVars(ParseState *pstate, List *names, List *vars) +List * +expandRelAttrs(ParseState *pstate, RangeTblEntry *rte) { + List *names, + *vars; List *te_list = NIL; + expandRTE(pstate, rte, &names, &vars); + while (names) { char *label = strVal(lfirst(names)); @@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars) return te_list; } -/* ---------- +/* * get_rte_attribute_name * Get an attribute name from a RangeTblEntry * * This is unlike get_attname() because we use aliases if available. - * In particular, it will work on an RTE for a subselect, whereas + * In particular, it will work on an RTE for a subselect or join, whereas * get_attname() only works on real relations. * * "*" is returned if the given attnum is InvalidAttrNumber --- this case * occurs when a Var represents a whole tuple of a relation. - * - * XXX Actually, this is completely bogus, because refnames of RTEs are - * not guaranteed unique, and may not even have scope across the whole - * query. Cleanest fix would be to add refname/attname to Var nodes and - * just print those, rather than indulging in this hack. - * ---------- */ char * get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) @@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) return "*"; /* - * If there is an alias, use it + * If there is an alias, use it. (This path should always be taken + * for non-relation RTEs.) */ if (attnum > 0 && attnum <= length(rte->eref->attrs)) return strVal(nth(attnum - 1, rte->eref->attrs)); @@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) /* * Can get here for a system attribute (which never has an alias), or * if alias name list is too short (which probably can't happen - * anymore). Neither of these cases is valid for a subselect RTE. + * anymore). Neither of these cases is valid for a non-relation RTE. */ - if (rte->relid == InvalidOid) + if (rte->rtekind != RTE_RELATION) elog(ERROR, "Invalid attnum %d for rangetable entry %s", attnum, rte->eref->relname); @@ -926,6 +959,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) } /* + * get_rte_attribute_type + * Get attribute type information from a RangeTblEntry + */ +void +get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, + Oid *vartype, int32 *vartypmod) +{ + if (rte->rtekind == RTE_RELATION) + { + /* Plain relation RTE --- get the attribute's type info */ + HeapTuple tp; + Form_pg_attribute att_tup; + + tp = SearchSysCache(ATTNUM, + ObjectIdGetDatum(rte->relid), + Int16GetDatum(attnum), + 0, 0); + /* this shouldn't happen... */ + if (!HeapTupleIsValid(tp)) + elog(ERROR, "Relation %s does not have attribute %d", + rte->relname, attnum); + att_tup = (Form_pg_attribute) GETSTRUCT(tp); + *vartype = att_tup->atttypid; + *vartypmod = att_tup->atttypmod; + ReleaseSysCache(tp); + } + else if (rte->rtekind == RTE_SUBQUERY) + { + /* Subselect RTE --- get type info from subselect's tlist */ + List *tlistitem; + + foreach(tlistitem, rte->subquery->targetList) + { + TargetEntry *te = (TargetEntry *) lfirst(tlistitem); + + if (te->resdom->resjunk || te->resdom->resno != attnum) + continue; + *vartype = te->resdom->restype; + *vartypmod = te->resdom->restypmod; + return; + } + /* falling off end of list shouldn't happen... */ + elog(ERROR, "Subquery %s does not have attribute %d", + rte->eref->relname, attnum); + } + else if (rte->rtekind == RTE_JOIN) + { + /* Join RTE --- get type info directly from join RTE */ + Assert(attnum > 0 && attnum <= length(rte->joincoltypes)); + *vartype = (Oid) nthi(attnum-1, rte->joincoltypes); + *vartypmod = nthi(attnum-1, rte->joincoltypmods); + } + else + elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d", + (int) rte->rtekind); +} + +/* * given relation and att name, return id of variable * * This should only be used if the relation is already diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bb398a7068..f5791298f3 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,12 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.76 2001/11/05 17:46:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.77 2002/03/12 00:51:56 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" + #include "nodes/makefuncs.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" @@ -118,30 +119,16 @@ transformTargetList(ParseState *pstate, List *targetlist) * Target item is relation.*, expand that table (eg. * SELECT emp.*, dname FROM emp, dept) */ - Node *rteorjoin; + RangeTblEntry *rte; int sublevels_up; - rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname, - &sublevels_up); - - if (rteorjoin == NULL) - { - rteorjoin = (Node *) addImplicitRTE(pstate, att->relname); - sublevels_up = 0; - } + rte = refnameRangeTblEntry(pstate, att->relname, + &sublevels_up); + if (rte == NULL) + rte = addImplicitRTE(pstate, att->relname); - if (IsA(rteorjoin, RangeTblEntry)) - p_target = nconc(p_target, - expandRelAttrs(pstate, - (RangeTblEntry *) rteorjoin)); - else if (IsA(rteorjoin, JoinExpr)) - p_target = nconc(p_target, - expandJoinAttrs(pstate, - (JoinExpr *) rteorjoin, - sublevels_up)); - else - elog(ERROR, "transformTargetList: unexpected node type %d", - nodeTag(rteorjoin)); + p_target = nconc(p_target, + expandRelAttrs(pstate, rte)); } else { @@ -405,34 +392,29 @@ ExpandAllTables(ParseState *pstate) foreach(ns, pstate->p_namespace) { Node *n = (Node *) lfirst(ns); + RangeTblEntry *rte; if (IsA(n, RangeTblRef)) - { - RangeTblEntry *rte; - rte = rt_fetch(((RangeTblRef *) n)->rtindex, pstate->p_rtable); - - /* - * Ignore added-on relations that were not listed in the FROM - * clause. - */ - if (!rte->inFromCl) - continue; - - target = nconc(target, expandRelAttrs(pstate, rte)); - } else if (IsA(n, JoinExpr)) - { - /* A newfangled join expression */ - JoinExpr *j = (JoinExpr *) n; - - /* Currently, a join expr could only have come from FROM. */ - target = nconc(target, expandJoinAttrs(pstate, j, 0)); - } + rte = rt_fetch(((JoinExpr *) n)->rtindex, + pstate->p_rtable); else + { elog(ERROR, "ExpandAllTables: unexpected node (internal error)" "\n\t%s", nodeToString(n)); + rte = NULL; /* keep compiler quiet */ + } + + /* + * Ignore added-on relations that were not listed in the FROM + * clause. + */ + if (!rte->inFromCl) + continue; + + target = nconc(target, expandRelAttrs(pstate, rte)); } /* Check for SELECT *; */ |
