parse_clause.c

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

C
1,591
字号
	 */	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;}/* * transformFromClauseItem - *	  Transform a FROM-clause item, adding any required entries to the *	  range table list being built in the ParseState, and return the *	  transformed item ready to include in the joinlist and namespace. *	  This routine can recurse to handle SQL92 JOIN expressions. * *	  Aside from the primary return value (the transformed joinlist item) *	  this routine also returns an integer list of the rangetable indexes *	  of all the base and join relations represented in the joinlist item. *	  This list is needed for checking JOIN/ON conditions in higher levels. */static Node *transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels){	if (IsA(n, RangeVar))	{		/* Plain relation reference */		RangeTblRef *rtr;		rtr = transformTableEntry(pstate, (RangeVar *) n);		*containedRels = makeListi1(rtr->rtindex);		return (Node *) rtr;	}	else if (IsA(n, RangeSubselect))	{		/* sub-SELECT is like a plain relation */		RangeTblRef *rtr;		rtr = transformRangeSubselect(pstate, (RangeSubselect *) n);		*containedRels = makeListi1(rtr->rtindex);		return (Node *) rtr;	}	else if (IsA(n, RangeFunction))	{		/* function is like a plain relation */		RangeTblRef *rtr;		rtr = transformRangeFunction(pstate, (RangeFunction *) n);		*containedRels = makeListi1(rtr->rtindex);		return (Node *) rtr;	}	else if (IsA(n, JoinExpr))	{		/* A newfangled join expression */		JoinExpr   *j = (JoinExpr *) n;		List	   *my_containedRels,				   *l_containedRels,				   *r_containedRels,				   *l_colnames,				   *r_colnames,				   *res_colnames,				   *l_colvars,				   *r_colvars,				   *res_colvars;		Index		leftrti,					rightrti;		RangeTblEntry *rte;		/*		 * Recursively process the left and right subtrees		 */		j->larg = transformFromClauseItem(pstate, j->larg, &l_containedRels);		j->rarg = transformFromClauseItem(pstate, j->rarg, &r_containedRels);		/*		 * Generate combined list of relation indexes for possible use by		 * transformJoinOnClause below.		 */		my_containedRels = nconc(l_containedRels, r_containedRels);		/*		 * Check for conflicting refnames in left and right subtrees. Must		 * do this because higher levels will assume I hand back a self-		 * consistent namespace subtree.		 */		checkNameSpaceConflicts(pstate, j->larg, j->rarg);		/*		 * Extract column name and var lists from both subtrees		 *		 * Note: expandRTE returns new lists, safe for me to modify		 */		if (IsA(j->larg, RangeTblRef))			leftrti = ((RangeTblRef *) j->larg)->rtindex;		else if (IsA(j->larg, JoinExpr))			leftrti = ((JoinExpr *) j->larg)->rtindex;		else		{			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg));			leftrti = 0;		/* keep compiler quiet */		}		rte = rt_fetch(leftrti, pstate->p_rtable);		expandRTE(pstate, rte, &l_colnames, &l_colvars);		if (IsA(j->rarg, RangeTblRef))			rightrti = ((RangeTblRef *) j->rarg)->rtindex;		else if (IsA(j->rarg, JoinExpr))			rightrti = ((JoinExpr *) j->rarg)->rtindex;		else		{			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg));			rightrti = 0;		/* keep compiler quiet */		}		rte = rt_fetch(rightrti, pstate->p_rtable);		expandRTE(pstate, rte, &r_colnames, &r_colvars);		/*		 * Natural join does not explicitly specify columns; must generate		 * columns to join. Need to run through the list of columns from		 * each table or join result and match up the column names. Use		 * the first table, and check every column in the second table for		 * a match.  (We'll check that the matches were unique later on.)		 * The result of this step is a list of column names just like an		 * explicitly-written USING list.		 */		if (j->isNatural)		{			List	   *rlist = NIL;			List	   *lx,					   *rx;			Assert(j->using == NIL);	/* shouldn't have USING() too */			foreach(lx, l_colnames)			{				char	   *l_colname = strVal(lfirst(lx));				Value	   *m_name = NULL;				foreach(rx, r_colnames)				{					char	   *r_colname = strVal(lfirst(rx));					if (strcmp(l_colname, r_colname) == 0)					{						m_name = makeString(l_colname);						break;					}				}				/* matched a right column? then keep as join column... */				if (m_name != NULL)					rlist = lappend(rlist, m_name);			}			j->using = rlist;		}		/*		 * Now transform the join qualifications, if any.		 */		res_colnames = NIL;		res_colvars = NIL;		if (j->using)		{			/*			 * JOIN/USING (or NATURAL JOIN, as transformed above).			 * Transform the list into an explicit ON-condition, and			 * generate a list of merged result columns.			 */			List	   *ucols = j->using;			List	   *l_usingvars = NIL;			List	   *r_usingvars = NIL;			List	   *ucol;			Assert(j->quals == NULL);	/* shouldn't have ON() too */			foreach(ucol, ucols)			{				char	   *u_colname = strVal(lfirst(ucol));				List	   *col;				int			ndx;				int			l_index = -1;				int			r_index = -1;				Var		   *l_colvar,						   *r_colvar;				/* Check for USING(foo,foo) */				foreach(col, res_colnames)				{					char	   *res_colname = strVal(lfirst(col));					if (strcmp(res_colname, u_colname) == 0)						ereport(ERROR,								(errcode(ERRCODE_DUPLICATE_COLUMN),								 errmsg("column name \"%s\" appears more than once in USING clause",										u_colname)));				}				/* Find it in left input */				ndx = 0;				foreach(col, l_colnames)				{					char	   *l_colname = strVal(lfirst(col));					if (strcmp(l_colname, u_colname) == 0)					{						if (l_index >= 0)							ereport(ERROR,									(errcode(ERRCODE_AMBIGUOUS_COLUMN),									 errmsg("common column name \"%s\" appears more than once in left table",											u_colname)));						l_index = ndx;					}					ndx++;				}				if (l_index < 0)					ereport(ERROR,							(errcode(ERRCODE_UNDEFINED_COLUMN),							 errmsg("column \"%s\" specified in USING clause does not exist in left table",									u_colname)));				/* Find it in right input */				ndx = 0;				foreach(col, r_colnames)				{					char	   *r_colname = strVal(lfirst(col));					if (strcmp(r_colname, u_colname) == 0)					{						if (r_index >= 0)							ereport(ERROR,									(errcode(ERRCODE_AMBIGUOUS_COLUMN),									 errmsg("common column name \"%s\" appears more than once in right table",											u_colname)));						r_index = ndx;					}					ndx++;				}				if (r_index < 0)					ereport(ERROR,							(errcode(ERRCODE_UNDEFINED_COLUMN),							 errmsg("column \"%s\" specified in USING clause does not exist in right table",									u_colname)));				l_colvar = nth(l_index, l_colvars);				l_usingvars = lappend(l_usingvars, l_colvar);				r_colvar = nth(r_index, r_colvars);				r_usingvars = lappend(r_usingvars, r_colvar);				res_colnames = lappend(res_colnames, lfirst(ucol));				res_colvars = lappend(res_colvars,									  buildMergedJoinVar(pstate,														 j->jointype,														 l_colvar,														 r_colvar));			}			j->quals = transformJoinUsingClause(pstate,												l_usingvars,												r_usingvars);		}		else if (j->quals)		{			/* User-written ON-condition; transform it */			j->quals = transformJoinOnClause(pstate, j, my_containedRels);		}		else		{			/* CROSS JOIN: no quals */		}		/* Add remaining columns from each side to the output columns */		extractRemainingColumns(res_colnames,								l_colnames, l_colvars,								&l_colnames, &l_colvars);		extractRemainingColumns(res_colnames,								r_colnames, r_colvars,								&r_colnames, &r_colvars);		res_colnames = nconc(res_colnames, l_colnames);		res_colvars = nconc(res_colvars, l_colvars);		res_colnames = nconc(res_colnames, r_colnames);		res_colvars = nconc(res_colvars, r_colvars);		/*		 * Check alias (AS clause), if any.		 */		if (j->alias)		{			if (j->alias->colnames != NIL)			{				if (length(j->alias->colnames) > length(res_colnames))					ereport(ERROR,							(errcode(ERRCODE_SYNTAX_ERROR),							 errmsg("column alias list for \"%s\" has too many entries",									j->alias->aliasname)));			}		}		/*		 * Now build an RTE for the result of the join		 */		rte = addRangeTableEntryForJoin(pstate,										res_colnames,										j->jointype,										res_colvars,										j->alias,										true);		/* assume new rte is at end */		j->rtindex = length(pstate->p_rtable);		Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable));		/*		 * Include join RTE in returned containedRels list		 */		*containedRels = lconsi(j->rtindex, my_containedRels);		return (Node *) j;	}	else		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n));	return NULL;				/* can't get here, keep compiler quiet */}/* * buildMergedJoinVar - *	  generate a suitable replacement expression for a merged join column */static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,				   Var *l_colvar, Var *r_colvar){	Oid			outcoltype;	int32		outcoltypmod;	Node	   *l_node,			   *r_node,			   *res_node;	/*	 * Choose output type if input types are dissimilar.	 */	outcoltype = l_colvar->vartype;	outcoltypmod = l_colvar->vartypmod;	if (outcoltype != r_colvar->vartype)	{		outcoltype = select_common_type(makeListo2(l_colvar->vartype,												   r_colvar->vartype),										"JOIN/USING");		outcoltypmod = -1;		/* ie, unknown */	}	else if (outcoltypmod != r_colvar->vartypmod)	{		/* same type, but not same typmod */		outcoltypmod = -1;		/* ie, unknown */	}	/*	 * Insert coercion functions if needed.  Note that a difference in	 * typmod can only happen if input has typmod but outcoltypmod is -1.	 * In that case we insert a RelabelType to clearly mark that result's	 * typmod is not same as input.	 */	if (l_colvar->vartype != outcoltype)		l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype,							 outcoltype,							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);	else if (l_colvar->vartypmod != outcoltypmod)		l_node = (Node *) makeRelabelType((Expr *) l_colvar,										  outcoltype, outcoltypmod,										  COERCE_IMPLICIT_CAST);	else		l_node = (Node *) l_colvar;	if (r_colvar->vartype != outcoltype)		r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype,							 outcoltype,							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);	else if (r_colvar->vartypmod != outcoltypmod)		r_node = (Node *) makeRelabelType((Expr *) r_colvar,										  outcoltype, outcoltypmod,										  COERCE_IMPLICIT_CAST);	else		r_node = (Node *) r_colvar;	/*	 * Choose what to emit	 */	switch (jointype)	{		case JOIN_INNER:			/*			 * We can use either var; prefer non-coerced one if available.			 */			if (IsA(l_node, Var))				res_node = l_node;			else if (IsA(r_node, Var))				res_node = r_node;			else				res_node = l_node;			break;		case JOIN_LEFT:			/* Always use left var */			res_node = l_node;			break;		case JOIN_RIGHT:			/* Always use right var */			res_node = r_node;			break;		case JOIN_FULL:			{				/*				 * Here we must build a COALESCE expression to ensure that				 * the join output is non-null if either input is.				 */				CoalesceExpr *c = makeNode(CoalesceExpr);				c->coalescetype = outcoltype;				c->args = makeList2(l_node, r_node);				res_node = (Node *) c;				break;			}		default:			elog(ERROR, "unrecognized join type: %d", (int) jointype);			res_node = NULL;	/* keep compiler quiet */			break;	}	return res_node;}/* * transformWhereClause - *	  Transform the qualification and make sure it is of type boolean. *	  Used for WHERE and allied clauses. * * constructName does not affect the semantics, but is used in error messages */Node *transformWhereClause(ParseState *pstate, Node *clause,					 const char *constructName){	Node	   *qual;	if (clause == NULL)		return NULL;	qual = transformExpr(pstate, clause);	qual = coerce_to_boolean(pstate, qual, constructName);	return qual;}/* * transformLimitClause - *	  Transform the expression and make sure it is of type integer. *	  Used for LIMIT and allied clauses. * * constructName does not affect the semantics, but is used in error messages */Node *transformLimitClause(ParseState *pstate, Node *clause,					 const char *constructName){	Node	   *qual;	if (clause == NULL)		return NULL;	qual = transformExpr(pstate, clause);	qual = coerce_to_integer(pstate, qual, constructName);	/*	 * LIMIT can't refer to any vars or aggregates of the current query;	 * we don't allow subselects either (though that case would at least	 * be sensible)	 */	if (contain_vars_of_level(qual, 0))	{		ereport(ERROR,				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),		/* translator: %s is name of a SQL construct, eg LIMIT */				 errmsg("argument of %s must not contain variables",						constructName)));	}	if (checkExprHasAggs(qual))	{		ereport(ERROR,				(errcode(ERRCODE_GROUPING_ERROR),		/* translator: %s is name of a SQL construct, eg LIMIT */				 errmsg("argument of %s must not contain aggregates",						constructName)));	}	if (contain_subplans(qual))	{		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),		/* translator: %s is name of a SQL construct, eg LIMIT */				 errmsg("argument of %s must not contain subqueries",						constructName)));	}	return qual;}/* *	findTargetlistEntry - *	  Returns the targetlist entry matching the given (untransformed) node. *	  If no matching entry exists, one is created and appended to the target *	  list as a "resjunk" node. * * node		the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched * tlist	the existing target list (NB: this will never be NIL, which is a *			good thing since we'd be unable to append to it if it were...) * clause	identifies clause type being processed. */static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause){	TargetEntry *target_result = NULL;	List	   *tl;	Node	   *expr;	/*----------

⌨️ 快捷键说明

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