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 + -
显示快捷键?