📄 parse_relation.c
字号:
/*------------------------------------------------------------------------- * * parse_relation.c * parser support routines dealing with relations * * Portions Copyright (c) 1996-2005, 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.116.2.2 2006/01/10 22:00:07 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include "access/heapam.h"#include "catalog/heap.h"#include "catalog/namespace.h"#include "catalog/pg_type.h"#include "funcapi.h"#include "nodes/makefuncs.h"#include "parser/parsetree.h"#include "parser/parse_expr.h"#include "parser/parse_relation.h"#include "parser/parse_type.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* GUC parameter */bool add_missing_from;static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, const char *refname);static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid);static bool isLockedRel(ParseState *pstate, char *refname);static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, bool include_dropped, List **colnames, List **colvars);static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, int rtindex, int sublevels_up, bool include_dropped, List **colnames, List **colvars);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) { RangeTblEntry *result; if (OidIsValid(relId)) result = scanNameSpaceForRelid(pstate, relId); else result = scanNameSpaceForRefname(pstate, refname); if (result) return result; if (sublevels_up) (*sublevels_up)++; else break; pstate = pstate->parentParseState; } return NULL;}/* * Search the query's table namespace for an RTE matching the * given unqualified refname. Return the RTE if a unique match, or NULL * if no match. Raise error if multiple matches. */static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, const char *refname){ RangeTblEntry *result = NULL; ListCell *l; foreach(l, pstate->p_relnamespace) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (strcmp(rte->eref->aliasname, refname) == 0) { if (result) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference \"%s\" is ambiguous", refname))); result = rte; } } return result;}/* * Search the query's table namespace for a relation RTE matching the * given relation OID. Return the RTE 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). * * See the comments for refnameRangeTblEntry to understand why this * acts the way it does. */static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid){ RangeTblEntry *result = NULL; ListCell *l; foreach(l, pstate->p_relnamespace) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); /* yes, the test for alias == NULL should be there... */ if (rte->rtekind == RTE_RELATION && rte->relid == relid && rte->alias == NULL) { if (result) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference %u is ambiguous", relid))); result = rte; } } return result;}/* * searchRangeTable * See if any RangeTblEntry could possibly match the RangeVar. * If so, return a pointer to the RangeTblEntry; else return NULL. * * This is different from refnameRangeTblEntry in that it considers every * entry in the ParseState's rangetable(s), not only those that are currently * visible in the p_relnamespace lists. This behavior is invalid per the SQL * spec, and it may give ambiguous results (there might be multiple equally * valid matches, but only one will be returned). This must be used ONLY * as a heuristic in giving suitable error messages. See warnAutoRange. * * Notice that we consider both matches on actual relation name and matches * on alias. */static RangeTblEntry *searchRangeTable(ParseState *pstate, RangeVar *relation){ Oid relId = RangeVarGetRelid(relation, true); char *refname = relation->relname; while (pstate != NULL) { ListCell *l; foreach(l, pstate->p_rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (OidIsValid(relId) && rte->rtekind == RTE_RELATION && rte->relid == relId) return rte; if (strcmp(rte->eref->aliasname, refname) == 0) return rte; } pstate = pstate->parentParseState; } return NULL;}/* * Check for relation-name conflicts between two relnamespace lists. * Raise an error if any is found. * * 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). */voidcheckNameSpaceConflicts(ParseState *pstate, List *namespace1, List *namespace2){ ListCell *l1; foreach(l1, namespace1) { RangeTblEntry *rte1 = (RangeTblEntry *) lfirst(l1); const char *aliasname1 = rte1->eref->aliasname; ListCell *l2; foreach(l2, namespace2) { RangeTblEntry *rte2 = (RangeTblEntry *) lfirst(l2); if (strcmp(rte2->eref->aliasname, aliasname1) != 0) continue; /* definitely no conflict */ if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL && rte2->rtekind == RTE_RELATION && rte2->alias == NULL && rte1->relid != rte2->relid) continue; /* no conflict per SQL92 rule */ ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("table name \"%s\" specified more than once", aliasname1))); } }}/* * 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. */intRTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up){ int index; ListCell *l; if (sublevels_up) *sublevels_up = 0; while (pstate != NULL) { index = 1; foreach(l, pstate->p_rtable) { if (rte == (RangeTblEntry *) lfirst(l)) 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 <= list_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; ListCell *c; /* * Scan the user column names (or aliases) for a match. Complain if * multiple matches. * * Note: eref->colnames may include entries for dropped columns, but those * will be empty strings that cannot match any legal SQL identifier, so we * don't bother to test for that case here. * * Should this somehow go wrong and we try to access 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 (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; while (pstate != NULL) { ListCell *l; foreach(l, pstate->p_varnamespace) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); Node *newresult; /* use orig_pstate here to get the right sublevels_up */ newresult = scanRTEForColumn(orig_pstate, rte, colname); 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; } 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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -