/*------------------------------------------------------------------------- * * parse_relation.c * parser support routines dealing with relations * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.94 2004/04/18 18:12:58 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "access/heapam.h" #include "access/htup.h" #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" /* GUC parameter */ bool add_missing_from; static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, const char *refname); static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid); static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, RangeTblEntry *rte1, const char *aliasname1); static bool isForUpdate(ParseState *pstate, char *refname); static bool get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum); static int specialAttNum(const char *attname); static void warnAutoRange(ParseState *pstate, RangeVar *relation); /* * refnameRangeTblEntry * Given a possibly-qualified refname, look to see if it matches any RTE. * If so, return a pointer to the RangeTblEntry; else return NULL. * * Optionally get RTE's nesting depth (0 = current) into *sublevels_up. * If sublevels_up is NULL, only consider items at the current nesting * level. * * An unqualified refname (schemaname == NULL) can match any RTE with matching * alias, or matching unqualified relname in the case of alias-less relation * RTEs. It is possible that such a refname matches multiple RTEs in the * nearest nesting level that has a match; if so, we report an error via * ereport(). * * A qualified refname (schemaname != NULL) can only match a relation RTE * that (a) has no alias and (b) is for the same relation identified by * schemaname.refname. In this case we convert schemaname.refname to a * relation OID and search by relid, rather than by alias name. This is * peculiar, but it's what SQL92 says to do. */ RangeTblEntry * refnameRangeTblEntry(ParseState *pstate, const char *schemaname, const char *refname, int *sublevels_up) { Oid relId = InvalidOid; if (sublevels_up) *sublevels_up = 0; if (schemaname != NULL) { Oid namespaceId; namespaceId = LookupExplicitNamespace(schemaname); relId = get_relname_relid(refname, namespaceId); if (!OidIsValid(relId)) return NULL; } while (pstate != NULL) { Node *nsnode; if (OidIsValid(relId)) nsnode = scanNameSpaceForRelid(pstate, (Node *) pstate->p_namespace, relId); else 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) (*sublevels_up)++; else break; } return NULL; } /* * Recursively search a namespace for an RTE or joinexpr matching the * given unqualified refname. Return the node if a unique match, or NULL * if no match. Raise error if multiple matches. * * The top level of p_namespace is a list, and we recurse into any joins * that are not subqueries. */ static Node * scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, const char *refname) { Node *result = NULL; Node *newresult; if (nsnode == NULL) return NULL; if (IsA(nsnode, RangeTblRef)) { int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (strcmp(rte->eref->aliasname, refname) == 0) result = (Node *) rte; } else if (IsA(nsnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) nsnode; if (j->alias) { if (strcmp(j->alias->aliasname, refname) == 0) return (Node *) j; /* matched a join alias */ /* * Tables within an aliased join are invisible from outside * the join, according to the scope rules of SQL92 (the join * is considered a subquery). So, stop here. */ return NULL; } result = scanNameSpaceForRefname(pstate, j->larg, refname); newresult = scanNameSpaceForRefname(pstate, j->rarg, refname); if (!result) result = newresult; else if (newresult) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference \"%s\" is ambiguous", refname))); } else if (IsA(nsnode, List)) { List *l; foreach(l, (List *) nsnode) { newresult = scanNameSpaceForRefname(pstate, lfirst(l), refname); if (!result) result = newresult; else if (newresult) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference \"%s\" is ambiguous", refname))); } } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode)); return result; } /* * Recursively search a namespace for a relation RTE matching the * given relation OID. Return the node if a unique match, or NULL * if no match. Raise error if multiple matches (which shouldn't * happen if the namespace was checked correctly when it was created). * * The top level of p_namespace is a list, and we recurse into any joins * that are not subqueries. * * See the comments for refnameRangeTblEntry to understand why this * acts the way it does. */ static Node * scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid) { Node *result = NULL; Node *newresult; if (nsnode == NULL) return NULL; if (IsA(nsnode, RangeTblRef)) { int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); /* yes, the test for alias==NULL should be there... */ if (rte->rtekind == RTE_RELATION && rte->relid == relid && rte->alias == NULL) result = (Node *) rte; } else if (IsA(nsnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) nsnode; if (j->alias) { /* * Tables within an aliased join are invisible from outside * the join, according to the scope rules of SQL92 (the join * is considered a subquery). So, stop here. */ return NULL; } result = scanNameSpaceForRelid(pstate, j->larg, relid); newresult = scanNameSpaceForRelid(pstate, j->rarg, relid); if (!result) result = newresult; else if (newresult) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference %u is ambiguous", relid))); } else if (IsA(nsnode, List)) { List *l; foreach(l, (List *) nsnode) { newresult = scanNameSpaceForRelid(pstate, lfirst(l), relid); if (!result) result = newresult; else if (newresult) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference %u is ambiguous", relid))); } } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode)); return result; } /* * Recursively check for name conflicts between two namespaces or * namespace subtrees. Raise an error if any is found. * * Works by recursively scanning namespace1 for RTEs and join nodes, * and for each one recursively scanning namespace2 for a match. * * Note: we assume that each given argument does not contain conflicts * itself; we just want to know if the two can be merged together. * * Per SQL92, two alias-less plain relation RTEs do not conflict even if * they have the same eref->aliasname (ie, same relation name), if they * are for different relation OIDs (implying they are in different schemas). */ void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, Node *namespace2) { if (namespace1 == NULL) return; if (IsA(namespace1, RangeTblRef)) { int varno = ((RangeTblRef *) namespace1)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (rte->rtekind == RTE_RELATION && rte->alias == NULL) scanNameSpaceForConflict(pstate, namespace2, rte, rte->eref->aliasname); else scanNameSpaceForConflict(pstate, namespace2, NULL, rte->eref->aliasname); } else if (IsA(namespace1, JoinExpr)) { JoinExpr *j = (JoinExpr *) namespace1; if (j->alias) { scanNameSpaceForConflict(pstate, namespace2, NULL, j->alias->aliasname); /* * Tables within an aliased join are invisible from outside * the join, according to the scope rules of SQL92 (the join * is considered a subquery). So, stop here. */ return; } checkNameSpaceConflicts(pstate, j->larg, namespace2); checkNameSpaceConflicts(pstate, j->rarg, namespace2); } else if (IsA(namespace1, List)) { List *l; foreach(l, (List *) namespace1) checkNameSpaceConflicts(pstate, lfirst(l), namespace2); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(namespace1)); } /* * Subroutine for checkNameSpaceConflicts: scan namespace2 */ static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, RangeTblEntry *rte1, const char *aliasname1) { if (nsnode == NULL) return; if (IsA(nsnode, RangeTblRef)) { int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (strcmp(rte->eref->aliasname, aliasname1) != 0) return; /* definitely no conflict */ if (rte->rtekind == RTE_RELATION && rte->alias == NULL && rte1 != NULL && rte->relid != rte1->relid) return; /* no conflict per SQL92 rule */ ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("table name \"%s\" specified more than once", aliasname1))); } else if (IsA(nsnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) nsnode; if (j->alias) { if (strcmp(j->alias->aliasname, aliasname1) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("table name \"%s\" specified more than once", aliasname1))); /* * Tables within an aliased join are invisible from outside * the join, according to the scope rules of SQL92 (the join * is considered a subquery). So, stop here. */ return; } scanNameSpaceForConflict(pstate, j->larg, rte1, aliasname1); scanNameSpaceForConflict(pstate, j->rarg, rte1, aliasname1); } else if (IsA(nsnode, List)) { List *l; foreach(l, (List *) nsnode) scanNameSpaceForConflict(pstate, lfirst(l), rte1, aliasname1); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(nsnode)); } /* * given an RTE, return RT index (starting with 1) of the entry, * and optionally get its nesting depth (0 = current). If sublevels_up * is NULL, only consider rels at the current nesting level. * Raises error if RTE not found. */ int RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up) { int index; List *temp; if (sublevels_up) *sublevels_up = 0; while (pstate != NULL) { index = 1; foreach(temp, pstate->p_rtable) { if (rte == (RangeTblEntry *) lfirst(temp)) return index; index++; } pstate = pstate->parentParseState; if (sublevels_up) (*sublevels_up)++; else break; } elog(ERROR, "RTE not found (internal error)"); 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. * If found, return an appropriate Var node, else return NULL. * If the name proves ambiguous within this RTE, raise error. * * 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 as requiring read access from the beginning. */ Node * scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname) { Node *result = NULL; int attnum = 0; List *c; /* * Scan the user column names (or aliases) for a match. Complain if * multiple matches. * * Note: because eref->colnames may include names of dropped columns, we * need to check for non-droppedness before accepting a match. This * takes an extra cache lookup, but we can skip the lookup most of the * time by exploiting the knowledge that dropped columns are assigned * dummy names starting with '.', which is an unusual choice for * actual column names. * * Should the user try to fool us by altering pg_attribute.attname for a * dropped column, we'll still catch it by virtue of the checks in * get_rte_attribute_type(), which is called by make_var(). That * routine has to do a cache lookup anyway, so the check there is * cheap. */ foreach(c, rte->eref->colnames) { attnum++; if (strcmp(strVal(lfirst(c)), colname) == 0) { if (colname[0] == '.' && /* see note above */ get_rte_attribute_is_dropped(rte, attnum)) continue; if (result) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("column reference \"%s\" is ambiguous", colname))); result = (Node *) make_var(pstate, rte, attnum); /* Require read access */ rte->requiredPerms |= ACL_SELECT; } } /* * If we have a unique match, return it. Note that this allows a user * alias to override a system column name (such as OID) without error. */ if (result) return result; /* * If the RTE represents a real table, consider system column names. */ if (rte->rtekind == RTE_RELATION) { /* quick check to see if name could be a system column */ attnum = specialAttNum(colname); if (attnum != InvalidAttrNumber) { /* now check to see if column actually is defined */ if (SearchSysCacheExists(ATTNUM, ObjectIdGetDatum(rte->relid), Int16GetDatum(attnum), 0, 0)) { result = (Node *) make_var(pstate, rte, attnum); /* Require read access */ rte->requiredPerms |= ACL_SELECT; } } } return result; } /* * colNameToVar * Search for an unqualified column name. * If found, return the appropriate Var node (or expression). * If not found, return NULL. If the name proves ambiguous, raise error. * If localonly is true, only names in the innermost query are considered. */ Node * colNameToVar(ParseState *pstate, char *colname, bool localonly) { Node *result = NULL; ParseState *orig_pstate = pstate; int levels_up = 0; while (pstate != NULL) { List *ns; /* * We need to look only at top-level namespace items, and even for * those, ignore RTEs that are marked as not inFromCl and not the * query's target relation. */ foreach(ns, pstate->p_namespace) { Node *nsnode = (Node *) lfirst(ns); Node *newresult = NULL; if (IsA(nsnode, RangeTblRef)) { int varno = ((RangeTblRef *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); if (!rte->inFromCl && rte != pstate->p_target_rangetblentry) continue; /* use orig_pstate here to get the right sublevels_up */ newresult = scanRTEForColumn(orig_pstate, rte, colname); } else if (IsA(nsnode, JoinExpr)) { int varno = ((JoinExpr *) nsnode)->rtindex; RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable); /* 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, "unrecognized node type: %d", (int) nodeTag(nsnode)); if (newresult) { if (result) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("column reference \"%s\" is ambiguous", colname))); result = newresult; } } if (result != NULL || localonly) break; /* found, or don't want to look at parent */ pstate = pstate->parentParseState; levels_up++; } return result; } /* * qualifiedNameToVar * Search for a qualified column name: either refname.colname or * schemaname.relname.colname. * * If found, return the appropriate Var node. * If not found, return NULL. If the name proves ambiguous, raise error. */ Node * qualifiedNameToVar(ParseState *pstate, char *schemaname, char *refname, char *colname, bool implicitRTEOK) { RangeTblEntry *rte; int sublevels_up; rte = refnameRangeTblEntry(pstate, schemaname, refname, &sublevels_up); if (rte == NULL) { if (!implicitRTEOK) return NULL; rte = addImplicitRTE(pstate, makeRangeVar(schemaname, refname)); } return scanRTEForColumn(pstate, rte, colname); } /* * Add an entry for a relation to the pstate's range table (p_rtable). * * If pstate is NULL, we just build an RTE and return it without adding it * to an rtable list. * * Note: formerly this checked for refname conflicts, but that's wrong. * Caller is responsible for checking for conflicts in the appropriate scope. */ RangeTblEntry * addRangeTableEntry(ParseState *pstate, RangeVar *relation, Alias *alias, bool inh, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; Alias *eref; int maxattrs; int numaliases; int varattno; rte->rtekind = RTE_RELATION; rte->alias = alias; /* * Get the rel's OID. This access also ensures that we have an * up-to-date relcache entry for the rel. Since this is typically the * first access to a rel in a statement, be careful to get the right * access level depending on whether we're doing SELECT FOR UPDATE. */ lockmode = isForUpdate(pstate, refname) ? RowShareLock : AccessShareLock; rel = heap_openrv(relation, lockmode); rte->relid = RelationGetRelid(rel); eref = alias ? (Alias *) copyObject(alias) : makeAlias(refname, NIL); numaliases = length(eref->colnames); /* * Since the rel is open anyway, let's check that the number of column * aliases is reasonable. - Thomas 2000-02-04 */ maxattrs = RelationGetNumberOfAttributes(rel); if (maxattrs < numaliases) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("table \"%s\" has %d columns available but %d columns specified", RelationGetRelationName(rel), maxattrs, numaliases))); /* fill in any unspecified alias columns using actual column names */ for (varattno = numaliases; varattno < maxattrs; varattno++) { char *attrname; attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); eref->colnames = lappend(eref->colnames, makeString(attrname)); } rte->eref = eref; /* * Drop the rel refcount, but keep the access lock till end of * transaction so that the table can't be deleted or have its schema * modified underneath us. */ heap_close(rel, NoLock); /*---------- * Flags: * - this RTE should be expanded to include descendant tables, * - this RTE is in the FROM clause, * - this RTE should be checked for appropriate access rights. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. *---------- */ rte->inh = inh; rte->inFromCl = inFromCl; rte->requiredPerms = ACL_SELECT; rte->checkAsUser = 0; /* not set-uid by default, either */ /* * 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; } /* * Add an entry for a relation to the pstate's range table (p_rtable). * * This is just like addRangeTableEntry() except that it makes an RTE * given a relation OID instead of a RangeVar reference. * * Note that an alias clause *must* be supplied. */ RangeTblEntry * addRangeTableEntryForRelation(ParseState *pstate, Oid relid, Alias *alias, bool inh, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias->aliasname; LOCKMODE lockmode; Relation rel; Alias *eref; int maxattrs; int numaliases; int varattno; rte->rtekind = RTE_RELATION; rte->alias = alias; /* * Get the rel's relcache entry. This access ensures that we have an * up-to-date relcache entry for the rel. Since this is typically the * first access to a rel in a statement, be careful to get the right * access level depending on whether we're doing SELECT FOR UPDATE. */ lockmode = isForUpdate(pstate, refname) ? RowShareLock : AccessShareLock; rel = heap_open(relid, lockmode); rte->relid = relid; eref = (Alias *) copyObject(alias); numaliases = length(eref->colnames); /* * Since the rel is open anyway, let's check that the number of column * aliases is reasonable. - Thomas 2000-02-04 */ maxattrs = RelationGetNumberOfAttributes(rel); if (maxattrs < numaliases) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("table \"%s\" has %d columns available but %d columns specified", RelationGetRelationName(rel), maxattrs, numaliases))); /* fill in any unspecified alias columns using actual column names */ for (varattno = numaliases; varattno < maxattrs; varattno++) { char *attrname; attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); eref->colnames = lappend(eref->colnames, makeString(attrname)); } rte->eref = eref; /* * Drop the rel refcount, but keep the access lock till end of * transaction so that the table can't be deleted or have its schema * modified underneath us. */ heap_close(rel, NoLock); /*---------- * Flags: * - this RTE should be expanded to include descendant tables, * - this RTE is in the FROM clause, * - this RTE should be checked for appropriate access rights. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. *---------- */ rte->inh = inh; rte->inFromCl = inFromCl; rte->requiredPerms = ACL_SELECT; rte->checkAsUser = 0; /* not set-uid by default, either */ /* * 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; } /* * Add an entry for a subquery to the pstate's range table (p_rtable). * * This is just like addRangeTableEntry() except that it makes a subquery RTE. * Note that an alias clause *must* be supplied. */ RangeTblEntry * addRangeTableEntryForSubquery(ParseState *pstate, Query *subquery, Alias *alias, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); char *refname = alias->aliasname; Alias *eref; int numaliases; int varattno; List *tlistitem; rte->rtekind = RTE_SUBQUERY; rte->relid = InvalidOid; rte->subquery = subquery; rte->alias = alias; eref = copyObject(alias); numaliases = length(eref->colnames); /* fill in any unspecified alias columns */ varattno = 0; foreach(tlistitem, subquery->targetList) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); if (te->resdom->resjunk) continue; varattno++; Assert(varattno == te->resdom->resno); if (varattno > numaliases) { char *attrname; attrname = pstrdup(te->resdom->resname); eref->colnames = lappend(eref->colnames, makeString(attrname)); } } if (varattno < numaliases) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("table \"%s\" has %d columns available but %d columns specified", refname, varattno, numaliases))); 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 appropriate access rights. * * Subqueries are never checked for access rights. *---------- */ rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; rte->requiredPerms = 0; rte->checkAsUser = 0; /* * 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; } /* * Add an entry for a function to the pstate's range table (p_rtable). * * This is just like addRangeTableEntry() except that it makes a function RTE. */ RangeTblEntry * addRangeTableEntryForFunction(ParseState *pstate, char *funcname, Node *funcexpr, RangeFunction *rangefunc, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); Oid funcrettype = exprType(funcexpr); char functyptype; Alias *alias = rangefunc->alias; List *coldeflist = rangefunc->coldeflist; Alias *eref; int numaliases; int varattno; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; rte->subquery = NULL; rte->funcexpr = funcexpr; rte->coldeflist = coldeflist; rte->alias = alias; eref = alias ? (Alias *) copyObject(alias) : makeAlias(funcname, NIL); rte->eref = eref; numaliases = length(eref->colnames); /* * Now determine if the function returns a simple or composite type, * and check/add column aliases. */ if (coldeflist != NIL) { /* * we *only* allow a coldeflist for functions returning a RECORD * pseudo-type */ if (funcrettype != RECORDOID) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is only allowed for functions returning \"record\""))); } else { /* * ... and a coldeflist is *required* for functions returning a * RECORD pseudo-type */ if (funcrettype == RECORDOID) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("a column definition list is required for functions returning \"record\""))); } functyptype = get_typtype(funcrettype); if (functyptype == 'c') { /* * Named composite data type, i.e. a table's row type */ Oid funcrelid = typeidTypeRelid(funcrettype); Relation rel; int maxattrs; if (!OidIsValid(funcrelid)) /* shouldn't happen if typtype is * 'c' */ elog(ERROR, "invalid typrelid for complex type %u", funcrettype); /* * Get the rel's relcache entry. This access ensures that we have * an up-to-date relcache entry for the rel. */ rel = relation_open(funcrelid, AccessShareLock); /* * Since the rel is open anyway, let's check that the number of * column aliases is reasonable. */ maxattrs = RelationGetNumberOfAttributes(rel); if (maxattrs < numaliases) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("table \"%s\" has %d columns available but %d columns specified", RelationGetRelationName(rel), maxattrs, numaliases))); /* fill in alias columns using actual column names */ for (varattno = numaliases; varattno < maxattrs; varattno++) { char *attrname; attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); eref->colnames = lappend(eref->colnames, makeString(attrname)); } /* * Drop the rel refcount, but keep the access lock till end of * transaction so that the table can't be deleted or have its * schema modified underneath us. */ relation_close(rel, NoLock); } else if (functyptype == 'b' || functyptype == 'd') { /* * Must be a base data type, i.e. scalar. Just add one alias * column named for the function. */ if (numaliases > 1) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("too many column aliases specified for function %s", funcname))); if (numaliases == 0) eref->colnames = makeList1(makeString(eref->aliasname)); } else if (functyptype == 'p' && funcrettype == RECORDOID) { List *col; /* Use the column definition list to form the alias list */ eref->colnames = NIL; foreach(col, coldeflist) { ColumnDef *n = lfirst(col); char *attrname; attrname = pstrdup(n->colname); eref->colnames = lappend(eref->colnames, makeString(attrname)); } } else ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function \"%s\" in FROM has unsupported return type", funcname))); /*---------- * Flags: * - this RTE should be expanded to include descendant tables, * - this RTE is in the FROM clause, * - this RTE should be checked for appropriate access rights. * * Functions are never checked for access rights (at least, not by * the RTE permissions mechanism). *---------- */ rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; rte->requiredPerms = 0; rte->checkAsUser = 0; /* * 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; } /* * 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 *aliasvars, Alias *alias, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); Alias *eref; int numaliases; rte->rtekind = RTE_JOIN; rte->relid = InvalidOid; rte->subquery = NULL; rte->jointype = jointype; rte->joinaliasvars = aliasvars; rte->alias = alias; eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL); numaliases = length(eref->colnames); /* fill in any unspecified alias columns */ if (numaliases < length(colnames)) { while (numaliases-- > 0) colnames = lnext(colnames); eref->colnames = nconc(eref->colnames, 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 appropriate access rights. * * Joins are never checked for access rights. *---------- */ rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; rte->requiredPerms = 0; rte->checkAsUser = 0; /* * 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 refname been selected FOR UPDATE? */ static bool isForUpdate(ParseState *pstate, char *refname) { /* Outer loop to check parent query levels as well as this one */ while (pstate != NULL) { if (pstate->p_forUpdate != NIL) { if (lfirst(pstate->p_forUpdate) == NULL) { /* all tables used in query */ return true; } else { /* just the named tables */ List *l; foreach(l, pstate->p_forUpdate) { char *rname = strVal(lfirst(l)); if (strcmp(refname, rname) == 0) return true; } } } pstate = pstate->parentParseState; } return false; } /* * Add the given RTE as a top-level entry in the pstate's join list * and/or name space list. (We assume caller has checked for any * namespace conflict.) */ void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, bool addToJoinList, bool addToNameSpace) { int rtindex = RTERangeTablePosn(pstate, rte, NULL); RangeTblRef *rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; if (addToJoinList) pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); if (addToNameSpace) pstate->p_namespace = lappend(pstate->p_namespace, rtr); } /* * Add a POSTQUEL-style implicit RTE. * * We assume caller has already checked that there is no RTE or join with * a conflicting name. */ RangeTblEntry * addImplicitRTE(ParseState *pstate, RangeVar *relation) { RangeTblEntry *rte; rte = addRangeTableEntry(pstate, relation, NULL, false, false); addRTEtoQuery(pstate, rte, true, true); warnAutoRange(pstate, relation); return rte; } /* expandRTE() * * Given a rangetable entry, create lists of its column names (aliases if * provided, else real names) and Vars for each column. Only user columns * are considered, since this is primarily used to expand '*' and determine * the contents of JOIN tables. * * If only one of the two kinds of output list is needed, pass NULL for the * output pointer for the unwanted one. */ void expandRTE(ParseState *pstate, RangeTblEntry *rte, List **colnames, List **colvars) { int rtindex, sublevels_up, varattno; if (colnames) *colnames = NIL; if (colvars) *colvars = NIL; /* Need the RT index of the entry for creating Vars */ rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); switch (rte->rtekind) { case RTE_RELATION: { /* Ordinary relation RTE */ Relation rel; int maxattrs; int numaliases; rel = heap_open(rte->relid, AccessShareLock); maxattrs = RelationGetNumberOfAttributes(rel); numaliases = length(rte->eref->colnames); for (varattno = 0; varattno < maxattrs; varattno++) { Form_pg_attribute attr = rel->rd_att->attrs[varattno]; if (attr->attisdropped) continue; if (colnames) { char *label; if (varattno < numaliases) label = strVal(nth(varattno, rte->eref->colnames)); else label = NameStr(attr->attname); *colnames = lappend(*colnames, makeString(pstrdup(label))); } if (colvars) { Var *varnode; varnode = makeVar(rtindex, attr->attnum, attr->atttypid, attr->atttypmod, sublevels_up); *colvars = lappend(*colvars, varnode); } } heap_close(rel, AccessShareLock); } break; case RTE_SUBQUERY: { /* Subquery RTE */ List *aliasp = rte->eref->colnames; List *tlistitem; varattno = 0; foreach(tlistitem, rte->subquery->targetList) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); if (te->resdom->resjunk) continue; varattno++; Assert(varattno == te->resdom->resno); if (colnames) { /* Assume there is one alias per target item */ char *label = strVal(lfirst(aliasp)); *colnames = lappend(*colnames, makeString(pstrdup(label))); aliasp = lnext(aliasp); } if (colvars) { Var *varnode; varnode = makeVar(rtindex, varattno, te->resdom->restype, te->resdom->restypmod, sublevels_up); *colvars = lappend(*colvars, varnode); } } } break; case RTE_FUNCTION: { /* Function RTE */ Oid funcrettype = exprType(rte->funcexpr); char functyptype = get_typtype(funcrettype); List *coldeflist = rte->coldeflist; if (functyptype == 'c') { /* * Composite data type, i.e. a table's row type Same * as ordinary relation RTE */ Oid funcrelid = typeidTypeRelid(funcrettype); Relation rel; int maxattrs; int numaliases; if (!OidIsValid(funcrelid)) /* shouldn't happen */ elog(ERROR, "invalid typrelid for complex type %u", funcrettype); rel = relation_open(funcrelid, AccessShareLock); maxattrs = RelationGetNumberOfAttributes(rel); numaliases = length(rte->eref->colnames); for (varattno = 0; varattno < maxattrs; varattno++) { Form_pg_attribute attr = rel->rd_att->attrs[varattno]; if (attr->attisdropped) continue; if (colnames) { char *label; if (varattno < numaliases) label = strVal(nth(varattno, rte->eref->colnames)); else label = NameStr(attr->attname); *colnames = lappend(*colnames, makeString(pstrdup(label))); } if (colvars) { Var *varnode; varnode = makeVar(rtindex, attr->attnum, attr->atttypid, attr->atttypmod, sublevels_up); *colvars = lappend(*colvars, varnode); } } relation_close(rel, AccessShareLock); } else if (functyptype == 'b' || functyptype == 'd') { /* * Must be a base data type, i.e. scalar */ if (colnames) *colnames = lappend(*colnames, lfirst(rte->eref->colnames)); if (colvars) { Var *varnode; varnode = makeVar(rtindex, 1, funcrettype, -1, sublevels_up); *colvars = lappend(*colvars, varnode); } } else if (functyptype == 'p' && funcrettype == RECORDOID) { List *col; int attnum = 0; foreach(col, coldeflist) { ColumnDef *colDef = lfirst(col); attnum++; if (colnames) { char *attrname; attrname = pstrdup(colDef->colname); *colnames = lappend(*colnames, makeString(attrname)); } if (colvars) { Var *varnode; Oid atttypid; atttypid = typenameTypeId(colDef->typename); varnode = makeVar(rtindex, attnum, atttypid, -1, sublevels_up); *colvars = lappend(*colvars, varnode); } } } else ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function in FROM has unsupported return type"))); } break; case RTE_JOIN: { /* Join RTE */ List *aliasp = rte->eref->colnames; List *aliasvars = rte->joinaliasvars; varattno = 0; while (aliasp) { Assert(aliasvars); varattno++; if (colnames) { char *label = strVal(lfirst(aliasp)); *colnames = lappend(*colnames, makeString(pstrdup(label))); } if (colvars) { Node *aliasvar = (Node *) lfirst(aliasvars); Var *varnode; varnode = makeVar(rtindex, varattno, exprType(aliasvar), exprTypmod(aliasvar), sublevels_up); *colvars = lappend(*colvars, varnode); } aliasp = lnext(aliasp); aliasvars = lnext(aliasvars); } Assert(aliasvars == NIL); } break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); } } /* * expandRelAttrs - * Workhorse for "*" expansion: produce a list of targetentries * for the attributes of the rte */ 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)); Node *varnode = (Node *) lfirst(vars); TargetEntry *te = makeNode(TargetEntry); te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++, exprType(varnode), exprTypmod(varnode), label, false); te->expr = (Expr *) varnode; te_list = lappend(te_list, te); names = lnext(names); vars = lnext(vars); } Assert(vars == NIL); /* lists not same length? */ 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 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. */ char * get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) { if (attnum == InvalidAttrNumber) return "*"; /* * If there is a user-written column alias, use it. */ if (rte->alias && attnum > 0 && attnum <= length(rte->alias->colnames)) return strVal(nth(attnum - 1, rte->alias->colnames)); /* * If the RTE is a relation, go to the system catalogs not the * eref->colnames list. This is a little slower but it will give the * right answer if the column has been renamed since the eref list was * built (which can easily happen for rules). */ if (rte->rtekind == RTE_RELATION) return get_relid_attribute_name(rte->relid, attnum); /* * Otherwise use the column name from eref. There should always be * one. */ if (attnum > 0 && attnum <= length(rte->eref->colnames)) return strVal(nth(attnum - 1, rte->eref->colnames)); /* else caller gave us a bogus attnum */ elog(ERROR, "invalid attnum %d for rangetable entry %s", attnum, rte->eref->aliasname); return NULL; /* keep compiler quiet */ } /* * get_rte_attribute_type * Get attribute type information from a RangeTblEntry */ void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, Oid *vartype, int32 *vartypmod) { switch (rte->rtekind) { case 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); if (!HeapTupleIsValid(tp)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, rte->relid); att_tup = (Form_pg_attribute) GETSTRUCT(tp); /* * If dropped column, pretend it ain't there. See notes * in scanRTEForColumn. */ if (att_tup->attisdropped) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", NameStr(att_tup->attname), get_rel_name(rte->relid)))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; ReleaseSysCache(tp); } break; case RTE_SUBQUERY: { /* Subselect RTE --- get type info from subselect's tlist */ TargetEntry *te = get_tle_by_resno(rte->subquery->targetList, attnum); if (te == NULL || te->resdom->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); *vartype = te->resdom->restype; *vartypmod = te->resdom->restypmod; } break; case RTE_FUNCTION: { /* Function RTE */ Oid funcrettype = exprType(rte->funcexpr); char functyptype = get_typtype(funcrettype); List *coldeflist = rte->coldeflist; if (functyptype == 'c') { /* * Composite data type, i.e. a table's row type Same * as ordinary relation RTE */ Oid funcrelid = typeidTypeRelid(funcrettype); HeapTuple tp; Form_pg_attribute att_tup; if (!OidIsValid(funcrelid)) /* shouldn't happen */ elog(ERROR, "invalid typrelid for complex type %u", funcrettype); tp = SearchSysCache(ATTNUM, ObjectIdGetDatum(funcrelid), Int16GetDatum(attnum), 0, 0); if (!HeapTupleIsValid(tp)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, funcrelid); att_tup = (Form_pg_attribute) GETSTRUCT(tp); /* * If dropped column, pretend it ain't there. See * notes in scanRTEForColumn. */ if (att_tup->attisdropped) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", NameStr(att_tup->attname), get_rel_name(funcrelid)))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; ReleaseSysCache(tp); } else if (functyptype == 'b' || functyptype == 'd') { /* * Must be a base data type, i.e. scalar */ *vartype = funcrettype; *vartypmod = -1; } else if (functyptype == 'p' && funcrettype == RECORDOID) { ColumnDef *colDef = nth(attnum - 1, coldeflist); *vartype = typenameTypeId(colDef->typename); *vartypmod = -1; } else ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function in FROM has unsupported return type"))); } break; case RTE_JOIN: { /* * Join RTE --- get type info from join RTE's alias * variable */ Node *aliasvar; Assert(attnum > 0 && attnum <= length(rte->joinaliasvars)); aliasvar = (Node *) nth(attnum - 1, rte->joinaliasvars); *vartype = exprType(aliasvar); *vartypmod = exprTypmod(aliasvar); } break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); } } /* * get_rte_attribute_is_dropped * Check whether attempted attribute ref is to a dropped column */ static bool get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) { bool result; switch (rte->rtekind) { case 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); if (!HeapTupleIsValid(tp)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, rte->relid); att_tup = (Form_pg_attribute) GETSTRUCT(tp); result = att_tup->attisdropped; ReleaseSysCache(tp); } break; case RTE_SUBQUERY: case RTE_JOIN: /* Subselect and join RTEs never have dropped columns */ result = false; break; case RTE_FUNCTION: { /* Function RTE */ Oid funcrettype = exprType(rte->funcexpr); Oid funcrelid = typeidTypeRelid(funcrettype); if (OidIsValid(funcrelid)) { /* * Composite data type, i.e. a table's row type Same * as ordinary relation RTE */ HeapTuple tp; Form_pg_attribute att_tup; tp = SearchSysCache(ATTNUM, ObjectIdGetDatum(funcrelid), Int16GetDatum(attnum), 0, 0); if (!HeapTupleIsValid(tp)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, funcrelid); att_tup = (Form_pg_attribute) GETSTRUCT(tp); result = att_tup->attisdropped; ReleaseSysCache(tp); } else { /* * Must be a base data type, i.e. scalar */ result = false; } } break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); result = false; /* keep compiler quiet */ } return result; } /* * Given a targetlist and a resno, return the matching TargetEntry * * Returns NULL if resno is not present in list. * * Note: we need to search, rather than just indexing with nth(), because * not all tlists are sorted by resno. */ TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno) { List *i; foreach(i, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(i); if (tle->resdom->resno == resno) return tle; } return NULL; } /* * given relation and att name, return id of variable * * This should only be used if the relation is already * heap_open()'ed. Use the cache version get_attnum() * for access to non-opened relations. */ int attnameAttNum(Relation rd, const char *attname, bool sysColOK) { int i; for (i = 0; i < rd->rd_rel->relnatts; i++) { Form_pg_attribute att = rd->rd_att->attrs[i]; if (namestrcmp(&(att->attname), attname) == 0 && !att->attisdropped) return i + 1; } if (sysColOK) { if ((i = specialAttNum(attname)) != InvalidAttrNumber) { if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids) return i; } } /* on failure */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", attname, RelationGetRelationName(rd)))); return InvalidAttrNumber; /* keep compiler quiet */ } /* specialAttNum() * * Check attribute name to see if it is "special", e.g. "oid". * - thomas 2000-02-07 * * Note: this only discovers whether the name could be a system attribute. * Caller needs to verify that it really is an attribute of the rel, * at least in the case of "oid", which is now optional. */ static int specialAttNum(const char *attname) { Form_pg_attribute sysatt; sysatt = SystemAttributeByName(attname, true /* "oid" will be accepted */ ); if (sysatt != NULL) return sysatt->attnum; return InvalidAttrNumber; } /* * given attribute id, return name of that attribute * * This should only be used if the relation is already * heap_open()'ed. Use the cache version get_atttype() * for access to non-opened relations. */ Name attnumAttName(Relation rd, int attid) { if (attid <= 0) { Form_pg_attribute sysatt; sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); return &sysatt->attname; } if (attid > rd->rd_att->natts) elog(ERROR, "invalid attribute number %d", attid); return &rd->rd_att->attrs[attid - 1]->attname; } /* * given attribute id, return type of that attribute * * This should only be used if the relation is already * heap_open()'ed. Use the cache version get_atttype() * for access to non-opened relations. */ Oid attnumTypeId(Relation rd, int attid) { if (attid <= 0) { Form_pg_attribute sysatt; sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); return sysatt->atttypid; } if (attid > rd->rd_att->natts) elog(ERROR, "invalid attribute number %d", attid); return rd->rd_att->attrs[attid - 1]->atttypid; } /* * Generate a warning or error about an implicit RTE, if appropriate. * * If ADD_MISSING_FROM is not enabled, raise an error. * * Our current theory on warnings is that we should allow "SELECT foo.*" * but warn about a mixture of explicit and implicit RTEs. */ static void warnAutoRange(ParseState *pstate, RangeVar *relation) { bool foundInFromCl = false; List *temp; if (!add_missing_from) { if (pstate->parentParseState != NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("missing FROM-clause entry in subquery for table \"%s\"", relation->relname))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("missing FROM-clause entry for table \"%s\"", relation->relname))); } foreach(temp, pstate->p_rtable) { RangeTblEntry *rte = lfirst(temp); if (rte->inFromCl) { foundInFromCl = true; break; } } if (foundInFromCl) { if (pstate->parentParseState != NULL) ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("adding missing FROM-clause entry in subquery for table \"%s\"", relation->relname))); else ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("adding missing FROM-clause entry for table \"%s\"", relation->relname))); } }