diff options
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r-- | src/backend/parser/parse_relation.c | 1451 |
1 files changed, 0 insertions, 1451 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c deleted file mode 100644 index 78a16ea08f8..00000000000 --- a/src/backend/parser/parse_relation.c +++ /dev/null @@ -1,1451 +0,0 @@ -/*------------------------------------------------------------------------- - * - * parse_relation.c - * parser support routines dealing with relations - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.70 2002/06/20 20:29:33 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <ctype.h> - -#include "access/heapam.h" -#include "access/htup.h" -#include "catalog/heap.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" - - -static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, - char *refname); -static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, - char *colname); -static bool isForUpdate(ParseState *pstate, char *refname); -static int specialAttNum(char *a); -static void warnAutoRange(ParseState *pstate, RangeVar *relation); - - -/* - * 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. - */ -RangeTblEntry * -refnameRangeTblEntry(ParseState *pstate, - char *refname, - int *sublevels_up) -{ - if (sublevels_up) - *sublevels_up = 0; - - while (pstate != NULL) - { - Node *nsnode; - - 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 with given refname. - * - * The top level of p_namespace is a list, and we recurse into any joins - * that are not subqueries. It is also possible to pass an individual - * join subtree (useful when checking for name conflicts within a scope). - * - * Note: we do not worry about the possibility of multiple matches; - * we assume the code that built the namespace checked for duplicates. - */ -static Node * -scanNameSpaceForRefname(ParseState *pstate, Node *nsnode, - char *refname) -{ - Node *result = NULL; - - 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); - if (!result) - result = scanNameSpaceForRefname(pstate, j->rarg, refname); - } - else if (IsA(nsnode, List)) - { - List *l; - - foreach(l, (List *) nsnode) - { - result = scanNameSpaceForRefname(pstate, lfirst(l), refname); - if (result) - break; - } - } - else - elog(ERROR, "scanNameSpaceForRefname: unexpected node type %d", - nodeTag(nsnode)); - return result; -} - -/* Convenience subroutine for checkNameSpaceConflicts */ -static void -scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, - char *refname) -{ - if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL) - elog(ERROR, "Table name \"%s\" specified more than once", refname); -} - -/* - * Recursively check for refname conflicts between two namespaces or - * namespace subtrees. Raise an error if any is found. - * - * Works by recursively scanning namespace1 in the same way that - * scanNameSpaceForRefname does, and then looking in namespace2 for - * a match to each refname found in namespace1. - * - * Note: we assume that each given argument does not contain conflicts - * itself; we just want to know if the two can be merged together. - */ -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); - - scanNameSpaceForConflict(pstate, namespace2, rte->eref->aliasname); - } - else if (IsA(namespace1, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) namespace1; - - if (j->alias) - { - scanNameSpaceForConflict(pstate, namespace2, 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, "checkNameSpaceConflicts: unexpected node type %d", - nodeTag(namespace1)); -} - -/* - * 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, "RTERangeTablePosn: RTE not found (internal error)"); - return 0; /* keep compiler quiet */ -} - -/* - * 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 checkForRead from the beginning. - */ -static 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. - */ - foreach(c, rte->eref->colnames) - { - attnum++; - if (strcmp(strVal(lfirst(c)), colname) == 0) - { - if (result) - elog(ERROR, "Column reference \"%s\" is ambiguous", colname); - result = (Node *) make_var(pstate, rte, attnum); - rte->checkForRead = true; - } - } - - /* - * 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); - rte->checkForRead = true; - } - } - } - - 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. - */ -Node * -colnameToVar(ParseState *pstate, char *colname) -{ - 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, "colnameToVar: unexpected node type %d", - nodeTag(nsnode)); - - if (newresult) - { - if (result) - elog(ERROR, "Column reference \"%s\" is ambiguous", - colname); - result = newresult; - } - } - - if (result != NULL) - break; /* found */ - - pstate = pstate->parentParseState; - levels_up++; - } - - return result; -} - -/* - * qualifiedNameToVar - * Search for a qualified column name (refname + column name). - * 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) -{ - RangeTblEntry *rte; - int sublevels_up; - - rte = refnameRangeTblEntry(pstate, refname, &sublevels_up); - - if (rte == NULL) - { - if (!implicitRTEOK) - return NULL; - rte = addImplicitRTE(pstate, makeRangeVar(NULL, 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) - elog(ERROR, "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 read/write 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->checkForRead = true; - rte->checkForWrite = false; - - rte->checkAsUser = InvalidOid; /* 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) - elog(ERROR, "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 read/write 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->checkForRead = true; - rte->checkForWrite = false; - - rte->checkAsUser = InvalidOid; /* 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) - elog(ERROR, "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 read/write access rights. - * - * Subqueries are never checked for access rights. - *---------- - */ - rte->inh = false; /* never true for subqueries */ - 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; -} - -/* - * 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, - Alias *alias, - bool inFromCl) -{ - RangeTblEntry *rte = makeNode(RangeTblEntry); - Oid funcrettype = exprType(funcexpr); - Oid funcrelid; - Alias *eref; - int numaliases; - int varattno; - - rte->rtekind = RTE_FUNCTION; - rte->relid = InvalidOid; - rte->subquery = NULL; - rte->funcexpr = funcexpr; - 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. - */ - funcrelid = typeidTypeRelid(funcrettype); - - if (OidIsValid(funcrelid)) - { - /* - * Composite data type, i.e. a table's row type - * - * Get the rel's relcache entry. This access ensures that we have an - * up-to-date relcache entry for the rel. - */ - Relation rel; - int maxattrs; - - rel = heap_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) - elog(ERROR, "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. - */ - heap_close(rel, NoLock); - } - else - { - /* - * Must be a base data type, i.e. scalar. - * Just add one alias column named for the function. - */ - if (numaliases > 1) - elog(ERROR, "Too many column aliases specified for function %s", - funcname); - if (numaliases == 0) - eref->colnames = makeList1(makeString(funcname)); - } - - /*---------- - * 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. - *---------- - */ - rte->inh = false; /* never true for functions */ - rte->inFromCl = inFromCl; - rte->checkForRead = true; - 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; -} - -/* - * 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 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 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 (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); - Oid funcrelid = typeidTypeRelid(funcrettype); - - if (OidIsValid(funcrelid)) - { - /* - * Composite data type, i.e. a table's row type - * Same as ordinary relation RTE - */ - Relation rel; - int maxattrs; - int numaliases; - - rel = heap_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 (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); - } - else - { - /* - * 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); - } - } - } - 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, "expandRTE: unsupported 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_last_resno)++, - exprType(varnode), - exprTypmod(varnode), - label, - false); - te->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) -{ - char *attname; - - if (attnum == InvalidAttrNumber) - return "*"; - - /* - * If there is an alias, use it. (This path should always be taken - * for non-relation RTEs.) - */ - if (attnum > 0 && attnum <= length(rte->eref->colnames)) - return strVal(nth(attnum - 1, rte->eref->colnames)); - - /* - * 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 non-relation RTE. - */ - if (rte->rtekind != RTE_RELATION) - elog(ERROR, "Invalid attnum %d for rangetable entry %s", - attnum, rte->eref->aliasname); - - /* - * Use the real name of the table's column - */ - attname = get_attname(rte->relid, attnum); - if (attname == NULL) - elog(ERROR, "cache lookup of attribute %d in relation %u failed", - attnum, rte->relid); - return attname; -} - -/* - * 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); - /* this shouldn't happen... */ - if (!HeapTupleIsValid(tp)) - elog(ERROR, "Relation %s does not have attribute %d", - get_rel_name(rte->relid), attnum); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - *vartype = att_tup->atttypid; - *vartypmod = att_tup->atttypmod; - ReleaseSysCache(tp); - } - break; - case 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->aliasname, attnum); - } - 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); - /* this shouldn't happen... */ - if (!HeapTupleIsValid(tp)) - elog(ERROR, "Relation %s does not have attribute %d", - get_rel_name(funcrelid), attnum); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - *vartype = att_tup->atttypid; - *vartypmod = att_tup->atttypmod; - ReleaseSysCache(tp); - } - else - { - /* - * Must be a base data type, i.e. scalar - */ - *vartype = funcrettype; - *vartypmod = -1; - } - } - 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, "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 - * heap_open()'ed. Use the cache version get_attnum() - * for access to non-opened relations. - */ -int -attnameAttNum(Relation rd, char *a) -{ - int i; - - for (i = 0; i < rd->rd_rel->relnatts; i++) - if (namestrcmp(&(rd->rd_att->attrs[i]->attname), a) == 0) - return i + 1; - - if ((i = specialAttNum(a)) != InvalidAttrNumber) - { - if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids) - return i; - } - - /* on failure */ - elog(ERROR, "Relation '%s' does not have attribute '%s'", - RelationGetRelationName(rd), a); - return InvalidAttrNumber; /* lint */ -} - -/* 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(char *a) -{ - Form_pg_attribute sysatt; - - sysatt = SystemAttributeByName(a, 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, "attnumAttName: 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, "attnumTypeId: invalid attribute number %d", attid); - return rd->rd_att->attrs[attid - 1]->atttypid; -} - -/* - * Generate a warning about an implicit RTE, if appropriate. - * - * Our current theory on this 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; - - foreach(temp, pstate->p_rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (rte->inFromCl) - { - foundInFromCl = true; - break; - } - } - if (foundInFromCl) - elog(NOTICE, "Adding missing FROM-clause entry%s for table \"%s\"", - pstate->parentParseState != NULL ? " in subquery" : "", - relation->relname); -} |