parse_clause.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,591 行 · 第 1/3 页

C
1,591
字号
/*------------------------------------------------------------------------- * * parse_clause.c *	  handle clauses in parser * * 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_clause.c,v 1.124.2.1 2004/04/18 18:13:31 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/heap.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/tlist.h"#include "optimizer/var.h"#include "parser/analyze.h"#include "parser/parsetree.h"#include "parser/parse_clause.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_relation.h"#include "parser/parse_target.h"#include "parser/parse_type.h"#include "rewrite/rewriteManip.h"#include "utils/builtins.h"#include "utils/guc.h"#define ORDER_CLAUSE 0#define GROUP_CLAUSE 1#define DISTINCT_ON_CLAUSE 2static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};static void extractRemainingColumns(List *common_colnames,						List *src_colnames, List *src_colvars,						List **res_colnames, List **res_colvars);static Node *transformJoinUsingClause(ParseState *pstate,						 List *leftVars, List *rightVars);static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,					  List *containedRels);static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r);static RangeTblRef *transformRangeSubselect(ParseState *pstate,						RangeSubselect *r);static RangeTblRef *transformRangeFunction(ParseState *pstate,					   RangeFunction *r);static Node *transformFromClauseItem(ParseState *pstate, Node *n,						List **containedRels);static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,				   Var *l_colvar, Var *r_colvar);static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,					List *tlist, int clause);/* * transformFromClause - *	  Process the FROM clause and add items to the query's range table, *	  joinlist, and namespace. * * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists * were initialized to NIL when the pstate was created.  We will add onto * any entries already present --- this is needed for rule processing, as * well as for UPDATE and DELETE. * * The range table may grow still further when we transform the expressions * in the query's quals and target list. (This is possible because in * POSTQUEL, we allowed references to relations not specified in the * from-clause.  PostgreSQL keeps this extension to standard SQL.) */voidtransformFromClause(ParseState *pstate, List *frmList){	List	   *fl;	/*	 * The grammar will have produced a list of RangeVars,	 * RangeSubselects, RangeFunctions, and/or JoinExprs. Transform each	 * one (possibly adding entries to the rtable), check for duplicate	 * refnames, and then add it to the joinlist and namespace.	 */	foreach(fl, frmList)	{		Node	   *n = lfirst(fl);		List	   *containedRels;		n = transformFromClauseItem(pstate, n, &containedRels);		checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n);		pstate->p_joinlist = lappend(pstate->p_joinlist, n);		pstate->p_namespace = lappend(pstate->p_namespace, n);	}}/* * setTargetTable *	  Add the target relation of INSERT/UPDATE/DELETE to the range table, *	  and make the special links to it in the ParseState. * *	  We also open the target relation and acquire a write lock on it. *	  This must be done before processing the FROM list, in case the target *	  is also mentioned as a source relation --- we want to be sure to grab *	  the write lock before any read lock. * *	  If alsoSource is true, add the target to the query's joinlist and *	  namespace.  For INSERT, we don't want the target to be joined to; *	  it's a destination of tuples, not a source.	For UPDATE/DELETE, *	  we do need to scan or join the target.  (NOTE: we do not bother *	  to check for namespace conflict; we assume that the namespace was *	  initially empty in these cases.) * *	  Returns the rangetable index of the target relation. */intsetTargetTable(ParseState *pstate, RangeVar *relation,			   bool inh, bool alsoSource){	RangeTblEntry *rte;	int			rtindex;	/* Close old target; this could only happen for multi-action rules */	if (pstate->p_target_relation != NULL)		heap_close(pstate->p_target_relation, NoLock);	/*	 * Open target rel and grab suitable lock (which we will hold till end	 * of transaction).	 *	 * analyze.c will eventually do the corresponding heap_close(), but *not*	 * release the lock.	 */	pstate->p_target_relation = heap_openrv(relation, RowExclusiveLock);	/*	 * Now build an RTE.	 */	rte = addRangeTableEntry(pstate, relation, NULL, inh, false);	pstate->p_target_rangetblentry = rte;	/* assume new rte is at end */	rtindex = length(pstate->p_rtable);	Assert(rte == rt_fetch(rtindex, pstate->p_rtable));	/*	 * Override addRangeTableEntry's default checkForRead, and instead	 * mark target table as requiring write access.	 *	 * If we find an explicit reference to the rel later during parse	 * analysis, scanRTEForColumn will change checkForRead to 'true'	 * again.  That can't happen for INSERT but it is possible for UPDATE	 * and DELETE.	 */	rte->checkForRead = false;	rte->checkForWrite = true;	/*	 * If UPDATE/DELETE, add table to joinlist and namespace.	 */	if (alsoSource)		addRTEtoQuery(pstate, rte, true, true);	return rtindex;}/* * Simplify InhOption (yes/no/default) into boolean yes/no. * * The reason we do things this way is that we don't want to examine the * SQL_inheritance option flag until parse_analyze is run.	Otherwise, * we'd do the wrong thing with query strings that intermix SET commands * with queries. */boolinterpretInhOption(InhOption inhOpt){	switch (inhOpt)	{		case INH_NO:			return false;		case INH_YES:			return true;		case INH_DEFAULT:			return SQL_inheritance;	}	elog(ERROR, "bogus InhOption value");	return false;				/* keep compiler quiet */}/* * Extract all not-in-common columns from column lists of a source table */static voidextractRemainingColumns(List *common_colnames,						List *src_colnames, List *src_colvars,						List **res_colnames, List **res_colvars){	List	   *new_colnames = NIL;	List	   *new_colvars = NIL;	List	   *lnames,			   *lvars = src_colvars;	foreach(lnames, src_colnames)	{		char	   *colname = strVal(lfirst(lnames));		bool		match = false;		List	   *cnames;		foreach(cnames, common_colnames)		{			char	   *ccolname = strVal(lfirst(cnames));			if (strcmp(colname, ccolname) == 0)			{				match = true;				break;			}		}		if (!match)		{			new_colnames = lappend(new_colnames, lfirst(lnames));			new_colvars = lappend(new_colvars, lfirst(lvars));		}		lvars = lnext(lvars);	}	*res_colnames = new_colnames;	*res_colvars = new_colvars;}/* transformJoinUsingClause() *	  Build a complete ON clause from a partially-transformed USING list. *	  We are given lists of nodes representing left and right match columns. *	  Result is a transformed qualification expression. */static Node *transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars){	Node	   *result = NULL;	List	   *lvars,			   *rvars = rightVars;	/*	 * We cheat a little bit here by building an untransformed operator	 * tree whose leaves are the already-transformed Vars.	This is OK	 * because transformExpr() won't complain about already-transformed	 * subnodes.	 */	foreach(lvars, leftVars)	{		Node	   *lvar = (Node *) lfirst(lvars);		Node	   *rvar = (Node *) lfirst(rvars);		A_Expr	   *e;		e = makeSimpleA_Expr(AEXPR_OP, "=", copyObject(lvar), copyObject(rvar));		if (result == NULL)			result = (Node *) e;		else		{			A_Expr	   *a;			a = makeA_Expr(AEXPR_AND, NIL, result, (Node *) e);			result = (Node *) a;		}		rvars = lnext(rvars);	}	/*	 * Since the references are already Vars, and are certainly from the	 * input relations, we don't have to go through the same pushups that	 * transformJoinOnClause() does.  Just invoke transformExpr() to fix	 * up the operators, and we're done.	 */	result = transformExpr(pstate, result);	result = coerce_to_boolean(pstate, result, "JOIN/USING");	return result;}	/* transformJoinUsingClause() *//* transformJoinOnClause() *	  Transform the qual conditions for JOIN/ON. *	  Result is a transformed qualification expression. */static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,					  List *containedRels){	Node	   *result;	List	   *save_namespace;	Relids		clause_varnos;	int			varno;	/*	 * This is a tad tricky, for two reasons.  First, the namespace that	 * the join expression should see is just the two subtrees of the JOIN	 * plus any outer references from upper pstate levels.	So,	 * temporarily set this pstate's namespace accordingly.  (We need not	 * check for refname conflicts, because transformFromClauseItem()	 * already did.) NOTE: this code is OK only because the ON clause	 * can't legally alter the namespace by causing implicit relation refs	 * to be added.	 */	save_namespace = pstate->p_namespace;	pstate->p_namespace = makeList2(j->larg, j->rarg);	result = transformWhereClause(pstate, j->quals, "JOIN/ON");	pstate->p_namespace = save_namespace;	/*	 * Second, we need to check that the ON condition doesn't refer to any	 * rels outside the input subtrees of the JOIN.  It could do that	 * despite our hack on the namespace if it uses fully-qualified names.	 * So, grovel through the transformed clause and make sure there are	 * no bogus references.  (Outer references are OK, and are ignored	 * here.)	 */	clause_varnos = pull_varnos(result);	while ((varno = bms_first_member(clause_varnos)) >= 0)	{		if (!intMember(varno, containedRels))		{			ereport(ERROR,					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),					 errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN",				   rt_fetch(varno, pstate->p_rtable)->eref->aliasname)));		}	}	bms_free(clause_varnos);	return result;}/* * transformTableEntry --- transform a RangeVar (simple relation reference) */static RangeTblRef *transformTableEntry(ParseState *pstate, RangeVar *r){	RangeTblEntry *rte;	RangeTblRef *rtr;	/*	 * mark this entry to indicate it comes from the FROM clause. In SQL,	 * the target list can only refer to range variables specified in the	 * from clause but we follow the more powerful POSTQUEL semantics and	 * automatically generate the range variable if not specified. However	 * there are times we need to know whether the entries are legitimate.	 */	rte = addRangeTableEntry(pstate, r, r->alias,							 interpretInhOption(r->inhOpt), true);	/*	 * We create a RangeTblRef, but we do not add it to the joinlist or	 * namespace; our caller must do that if appropriate.	 */	rtr = makeNode(RangeTblRef);	/* assume new rte is at end */	rtr->rtindex = length(pstate->p_rtable);	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));	return rtr;}/* * transformRangeSubselect --- transform a sub-SELECT appearing in FROM */static RangeTblRef *transformRangeSubselect(ParseState *pstate, RangeSubselect *r){	List	   *parsetrees;	Query	   *query;	RangeTblEntry *rte;	RangeTblRef *rtr;	/*	 * We require user to supply an alias for a subselect, per SQL92. To	 * relax this, we'd have to be prepared to gin up a unique alias for	 * an unlabeled subselect.	 */	if (r->alias == NULL)		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("subquery in FROM must have an alias")));	/*	 * Analyze and transform the subquery.	 */	parsetrees = parse_sub_analyze(r->subquery, pstate);	/*	 * Check that we got something reasonable.	Most of these conditions	 * are probably impossible given restrictions of the grammar, but	 * check 'em anyway.	 */	if (length(parsetrees) != 1)		elog(ERROR, "unexpected parse analysis result for subquery in FROM");	query = (Query *) lfirst(parsetrees);	if (query == NULL || !IsA(query, Query))		elog(ERROR, "unexpected parse analysis result for subquery in FROM");	if (query->commandType != CMD_SELECT)		elog(ERROR, "expected SELECT query from subquery in FROM");	if (query->resultRelation != 0 || query->into != NULL)		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("subquery in FROM may not have SELECT INTO")));	/*	 * The subquery cannot make use of any variables from FROM items	 * created earlier in the current query.  Per SQL92, the scope of a	 * FROM item does not include other FROM items.  Formerly we hacked	 * the namespace so that the other variables weren't even visible, but	 * it seems more useful to leave them visible and give a specific	 * error message.	 *	 * XXX this will need further work to support SQL99's LATERAL() feature,	 * wherein such references would indeed be legal.	 *	 * We can skip groveling through the subquery if there's not anything	 * visible in the current query.  Also note that outer references are	 * OK.	 */	if (pstate->p_namespace)	{		if (contain_vars_of_level((Node *) query, 1))			ereport(ERROR,					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),					 errmsg("subquery in FROM may not refer to other relations of same query level")));	}	/*	 * OK, build an RTE for the subquery.	 */	rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true);	/*	 * We create a RangeTblRef, but we do not add it to the joinlist or	 * namespace; our caller must do that if appropriate.	 */	rtr = makeNode(RangeTblRef);	/* assume new rte is at end */	rtr->rtindex = length(pstate->p_rtable);	Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));	return rtr;}/* * transformRangeFunction --- transform a function call appearing in FROM */static RangeTblRef *transformRangeFunction(ParseState *pstate, RangeFunction *r){	Node	   *funcexpr;	char	   *funcname;	RangeTblEntry *rte;	RangeTblRef *rtr;	/* Get function name for possible use as alias */	Assert(IsA(r->funccallnode, FuncCall));	funcname = strVal(llast(((FuncCall *) r->funccallnode)->funcname));	/*	 * Transform the raw FuncCall node.	 */	funcexpr = transformExpr(pstate, r->funccallnode);	/*	 * The function parameters cannot make use of any variables from other	 * FROM items.	(Compare to transformRangeSubselect(); the coding is	 * different though because we didn't parse as a sub-select with its	 * own level of namespace.)	 *	 * XXX this will need further work to support SQL99's LATERAL() feature,	 * wherein such references would indeed be legal.	 */	if (pstate->p_namespace)	{		if (contain_vars_of_level(funcexpr, 0))			ereport(ERROR,					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),					 errmsg("function expression in FROM may not refer to other relations of same query level")));	}	/*	 * Disallow aggregate functions in the expression.	(No reason to	 * postpone this check until parseCheckAggregates.)	 */	if (pstate->p_hasAggs)	{		if (checkExprHasAggs(funcexpr))			ereport(ERROR,					(errcode(ERRCODE_GROUPING_ERROR),					 errmsg("cannot use aggregate function in function expression in FROM")));	}	/*	 * If a coldeflist is supplied, ensure it defines a legal set of names	 * (no duplicates) and datatypes (no pseudo-types, for instance).	 */	if (r->coldeflist)	{		TupleDesc	tupdesc;		tupdesc = BuildDescForRelation(r->coldeflist);		CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE);	}	/*	 * OK, build an RTE for the function.	 */	rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,										r, true);	/*	 * We create a RangeTblRef, but we do not add it to the joinlist or	 * namespace; our caller must do that if appropriate.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?