⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 parse_relation.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
/*------------------------------------------------------------------------- * * 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 + -