diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-12 00:52:10 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-03-12 00:52:10 +0000 |
commit | 6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272 (patch) | |
tree | 5f209c5926768472f9d4fd7210065c18831dbd9c /src/backend/parser/parse_relation.c | |
parent | 66b6bf67a197db73a880d2a4d9dab05168cde8e0 (diff) | |
download | postgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.tar.gz postgresql-6eeb95f0f56bb5e8a0a9328aeec04c9e6de87272.zip |
Restructure representation of join alias variables. An explicit JOIN
now has an RTE of its own, and references to its outputs now are Vars
referencing the JOIN RTE, rather than CASE-expressions. This allows
reverse-listing in ruleutils.c to use the correct alias easily, rather
than painfully reverse-engineering the alias namespace as it used to do.
Also, nested FULL JOINs work correctly, because the result of the inner
joins are simple Vars that the planner can cope with. This fixes a bug
reported a couple times now, notably by Tatsuo on 18-Nov-01. The alias
Vars are expanded into COALESCE expressions where needed at the very end
of planning, rather than during parsing.
Also, beginnings of support for showing plan qualifier expressions in
EXPLAIN. There are probably still cases that need work.
initdb forced due to change of stored-rule representation.
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r-- | src/backend/parser/parse_relation.c | 357 |
1 files changed, 224 insertions, 133 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 9440914a776..1609c89ce07 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 |