parse_relation.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,944 行 · 第 1/4 页
C
1,944 行
/*------------------------------------------------------------------------- * * 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 * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.90.2.1 2004/04/18 18:13:31 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#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 Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname);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). */voidcheckNameSpaceConflicts(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 voidscanNameSpaceForConflict(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. */intRTERangeTablePosn(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 */}/* * 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. * * 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); 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.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?