parse_clause.c

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

C
1,591
字号
	 * Handle two special cases as mandated by the SQL92 spec:	 *	 * 1. Bare ColumnName (no qualifier or subscripts)	 *	  For a bare identifier, we search for a matching column name	 *	  in the existing target list.	Multiple matches are an error	 *	  unless they refer to identical values; for example,	 *	  we allow	SELECT a, a FROM table ORDER BY a	 *	  but not	SELECT a AS b, b FROM table ORDER BY b	 *	  If no match is found, we fall through and treat the identifier	 *	  as an expression.	 *	  For GROUP BY, it is incorrect to match the grouping item against	 *	  targetlist entries: according to SQL92, an identifier in GROUP BY	 *	  is a reference to a column name exposed by FROM, not to a target	 *	  list column.	However, many implementations (including pre-7.0	 *	  PostgreSQL) accept this anyway.  So for GROUP BY, we look first	 *	  to see if the identifier matches any FROM column name, and only	 *	  try for a targetlist name if it doesn't.  This ensures that we	 *	  adhere to the spec in the case where the name could be both.	 *	  DISTINCT ON isn't in the standard, so we can do what we like there;	 *	  we choose to make it work like ORDER BY, on the rather flimsy	 *	  grounds that ordinary DISTINCT works on targetlist entries.	 *	 * 2. IntegerConstant	 *	  This means to use the n'th item in the existing target list.	 *	  Note that it would make no sense to order/group/distinct by an	 *	  actual constant, so this does not create a conflict with our	 *	  extension to order/group by an expression.	 *	  GROUP BY column-number is not allowed by SQL92, but since	 *	  the standard has no other behavior defined for this syntax,	 *	  we may as well accept this common extension.	 *	 * Note that pre-existing resjunk targets must not be used in either case,	 * since the user didn't write them in his SELECT list.	 *	 * If neither special case applies, fall through to treat the item as	 * an expression.	 *----------	 */	if (IsA(node, ColumnRef) &&		length(((ColumnRef *) node)->fields) == 1 &&		((ColumnRef *) node)->indirection == NIL)	{		char	   *name = strVal(lfirst(((ColumnRef *) node)->fields));		if (clause == GROUP_CLAUSE)		{			/*			 * In GROUP BY, we must prefer a match against a FROM-clause			 * column to one against the targetlist.  Look to see if there			 * is a matching column.  If so, fall through to let			 * transformExpr() do the rest.  NOTE: if name could refer			 * ambiguously to more than one column name exposed by FROM,			 * colNameToVar will ereport(ERROR).  That's just what we want			 * here.			 *			 * Small tweak for 7.4.3: ignore matches in upper query levels.			 * This effectively changes the search order for bare names to			 * (1) local FROM variables, (2) local targetlist aliases,			 * (3) outer FROM variables, whereas before it was (1) (3) (2).			 * SQL92 and SQL99 do not allow GROUPing BY an outer reference,			 * so this breaks no cases that are legal per spec, and it			 * seems a more self-consistent behavior.			 */			if (colNameToVar(pstate, name, true) != NULL)				name = NULL;		}		if (name != NULL)		{			foreach(tl, tlist)			{				TargetEntry *tle = (TargetEntry *) lfirst(tl);				Resdom	   *resnode = tle->resdom;				if (!resnode->resjunk &&					strcmp(resnode->resname, name) == 0)				{					if (target_result != NULL)					{						if (!equal(target_result->expr, tle->expr))							ereport(ERROR,									(errcode(ERRCODE_AMBIGUOUS_COLUMN),									 /* translator: first %s is name of a SQL construct, eg ORDER BY */									 errmsg("%s \"%s\" is ambiguous",											clauseText[clause], name)));					}					else						target_result = tle;					/* Stay in loop to check for ambiguity */				}			}			if (target_result != NULL)				return target_result;	/* return the first match */		}	}	if (IsA(node, A_Const))	{		Value	   *val = &((A_Const *) node)->val;		int			targetlist_pos = 0;		int			target_pos;		if (!IsA(val, Integer))			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),			/* translator: %s is name of a SQL construct, eg ORDER BY */					 errmsg("non-integer constant in %s",							clauseText[clause])));		target_pos = intVal(val);		foreach(tl, tlist)		{			TargetEntry *tle = (TargetEntry *) lfirst(tl);			Resdom	   *resnode = tle->resdom;			if (!resnode->resjunk)			{				if (++targetlist_pos == target_pos)					return tle; /* return the unique match */			}		}		ereport(ERROR,				(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),		/* translator: %s is name of a SQL construct, eg ORDER BY */				 errmsg("%s position %d is not in select list",						clauseText[clause], target_pos)));	}	/*	 * Otherwise, we have an expression (this is a Postgres extension not	 * found in SQL92).  Convert the untransformed node to a transformed	 * expression, and search for a match in the tlist. NOTE: it doesn't	 * really matter whether there is more than one match.	Also, we are	 * willing to match a resjunk target here, though the above cases must	 * ignore resjunk targets.	 */	expr = transformExpr(pstate, node);	foreach(tl, tlist)	{		TargetEntry *tle = (TargetEntry *) lfirst(tl);		if (equal(expr, tle->expr))			return tle;	}	/*	 * If no matches, construct a new target entry which is appended to	 * the end of the target list.	This target is given resjunk = TRUE so	 * that it will not be projected into the final tuple.	 */	target_result = transformTargetEntry(pstate, node, expr, NULL, true);	lappend(tlist, target_result);	return target_result;}/* * transformGroupClause - *	  transform a GROUP BY clause */List *transformGroupClause(ParseState *pstate, List *grouplist,					 List *targetlist, List *sortClause){	List	   *glist = NIL,			   *gl;	foreach(gl, grouplist)	{		TargetEntry *tle;		Oid			restype;		Oid			ordering_op;		GroupClause *grpcl;		tle = findTargetlistEntry(pstate, lfirst(gl),								  targetlist, GROUP_CLAUSE);		/* avoid making duplicate grouplist entries */		if (targetIsInSortList(tle, glist))			continue;		/* if tlist item is an UNKNOWN literal, change it to TEXT */		restype = tle->resdom->restype;		if (restype == UNKNOWNOID)		{			tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,											 restype, TEXTOID,											 COERCION_IMPLICIT,											 COERCE_IMPLICIT_CAST);			restype = tle->resdom->restype = TEXTOID;			tle->resdom->restypmod = -1;		}		/*		 * If the GROUP BY clause matches the ORDER BY clause, we want to		 * adopt the ordering operators from the latter rather than using		 * the default ops.  This allows "GROUP BY foo ORDER BY foo DESC"		 * to be done with only one sort step.	Note we are assuming that		 * any user-supplied ordering operator will bring equal values		 * together, which is all that GROUP BY needs.		 */		if (sortClause &&			((SortClause *) lfirst(sortClause))->tleSortGroupRef ==			tle->resdom->ressortgroupref)		{			ordering_op = ((SortClause *) lfirst(sortClause))->sortop;			sortClause = lnext(sortClause);		}		else		{			ordering_op = ordering_oper_opid(restype);			sortClause = NIL;	/* disregard ORDER BY once match fails */		}		grpcl = makeNode(GroupClause);		grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);		grpcl->sortop = ordering_op;		glist = lappend(glist, grpcl);	}	return glist;}/* * transformSortClause - *	  transform an ORDER BY clause */List *transformSortClause(ParseState *pstate,					List *orderlist,					List *targetlist,					bool resolveUnknown){	List	   *sortlist = NIL;	List	   *olitem;	foreach(olitem, orderlist)	{		SortBy	   *sortby = lfirst(olitem);		TargetEntry *tle;		tle = findTargetlistEntry(pstate, sortby->node,								  targetlist, ORDER_CLAUSE);		sortlist = addTargetToSortList(pstate, tle,									   sortlist, targetlist,									   sortby->sortby_kind,									   sortby->useOp,									   resolveUnknown);	}	return sortlist;}/* * transformDistinctClause - *	  transform a DISTINCT or DISTINCT ON clause * * Since we may need to add items to the query's sortClause list, that list * is passed by reference.	We might also need to add items to the query's * targetlist, but we assume that cannot be empty initially, so we can * lappend to it even though the pointer is passed by value. */List *transformDistinctClause(ParseState *pstate, List *distinctlist,						List *targetlist, List **sortClause){	List	   *result = NIL;	List	   *slitem;	List	   *dlitem;	/* No work if there was no DISTINCT clause */	if (distinctlist == NIL)		return NIL;	if (lfirst(distinctlist) == NIL)	{		/* We had SELECT DISTINCT */		/*		 * All non-resjunk elements from target list that are not already		 * in the sort list should be added to it.	(We don't really care		 * what order the DISTINCT fields are checked in, so we can leave		 * the user's ORDER BY spec alone, and just add additional sort		 * keys to it to ensure that all targetlist items get sorted.)		 */		*sortClause = addAllTargetsToSortList(pstate,											  *sortClause,											  targetlist,											  true);		/*		 * Now, DISTINCT list consists of all non-resjunk sortlist items.		 * Actually, all the sortlist items had better be non-resjunk!		 * Otherwise, user wrote SELECT DISTINCT with an ORDER BY item		 * that does not appear anywhere in the SELECT targetlist, and we		 * can't implement that with only one sorting pass...		 */		foreach(slitem, *sortClause)		{			SortClause *scl = (SortClause *) lfirst(slitem);			TargetEntry *tle = get_sortgroupclause_tle(scl, targetlist);			if (tle->resdom->resjunk)				ereport(ERROR,						(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),						 errmsg("for SELECT DISTINCT, ORDER BY expressions must appear in select list")));			else				result = lappend(result, copyObject(scl));		}	}	else	{		/* We had SELECT DISTINCT ON (expr, ...) */		/*		 * If the user writes both DISTINCT ON and ORDER BY, then the two		 * expression lists must match (until one or the other runs out).		 * Otherwise the ORDER BY requires a different sort order than the		 * DISTINCT does, and we can't implement that with only one sort		 * pass (and if we do two passes, the results will be rather		 * unpredictable). However, it's OK to have more DISTINCT ON		 * expressions than ORDER BY expressions; we can just add the		 * extra DISTINCT values to the sort list, much as we did above		 * for ordinary DISTINCT fields.		 *		 * Actually, it'd be OK for the common prefixes of the two lists to		 * match in any order, but implementing that check seems like more		 * trouble than it's worth.		 */		List	   *nextsortlist = *sortClause;		foreach(dlitem, distinctlist)		{			TargetEntry *tle;			tle = findTargetlistEntry(pstate, lfirst(dlitem),									  targetlist, DISTINCT_ON_CLAUSE);			if (nextsortlist != NIL)			{				SortClause *scl = (SortClause *) lfirst(nextsortlist);				if (tle->resdom->ressortgroupref != scl->tleSortGroupRef)					ereport(ERROR,							(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),							 errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions")));				result = lappend(result, copyObject(scl));				nextsortlist = lnext(nextsortlist);			}			else			{				*sortClause = addTargetToSortList(pstate, tle,												  *sortClause, targetlist,												  SORTBY_ASC, NIL, true);				/*				 * Probably, the tle should always have been added at the				 * end of the sort list ... but search to be safe.				 */				foreach(slitem, *sortClause)				{					SortClause *scl = (SortClause *) lfirst(slitem);					if (tle->resdom->ressortgroupref == scl->tleSortGroupRef)					{						result = lappend(result, copyObject(scl));						break;					}				}				if (slitem == NIL)		/* should not happen */					elog(ERROR, "failed to add DISTINCT ON clause to target list");			}		}	}	return result;}/* * addAllTargetsToSortList *		Make sure all non-resjunk targets in the targetlist are in the *		ORDER BY list, adding the not-yet-sorted ones to the end of the list. *		This is typically used to help implement SELECT DISTINCT. * * See addTargetToSortList for info about pstate and resolveUnknown inputs. * * Returns the updated ORDER BY list. */List *addAllTargetsToSortList(ParseState *pstate, List *sortlist,						List *targetlist, bool resolveUnknown){	List	   *i;	foreach(i, targetlist)	{		TargetEntry *tle = (TargetEntry *) lfirst(i);		if (!tle->resdom->resjunk)			sortlist = addTargetToSortList(pstate, tle,										   sortlist, targetlist,										   SORTBY_ASC, NIL,										   resolveUnknown);	}	return sortlist;}/* * addTargetToSortList *		If the given targetlist entry isn't already in the ORDER BY list, *		add it to the end of the list, using the sortop with given name *		or the default sort operator if opname == NIL. * * If resolveUnknown is TRUE, convert TLEs of type UNKNOWN to TEXT.  If not, * do nothing (which implies the search for a sort operator will fail). * pstate should be provided if resolveUnknown is TRUE, but can be NULL * otherwise. * * Returns the updated ORDER BY list. */List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,					List *sortlist, List *targetlist,					int sortby_kind, List *sortby_opname,					bool resolveUnknown){	/* avoid making duplicate sortlist entries */	if (!targetIsInSortList(tle, sortlist))	{		SortClause *sortcl = makeNode(SortClause);		Oid			restype = tle->resdom->restype;		/* if tlist item is an UNKNOWN literal, change it to TEXT */		if (restype == UNKNOWNOID && resolveUnknown)		{			tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,											 restype, TEXTOID,											 COERCION_IMPLICIT,											 COERCE_IMPLICIT_CAST);			restype = tle->resdom->restype = TEXTOID;			tle->resdom->restypmod = -1;		}		sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);		switch (sortby_kind)		{			case SORTBY_ASC:				sortcl->sortop = ordering_oper_opid(restype);				break;			case SORTBY_DESC:				sortcl->sortop = reverse_ordering_oper_opid(restype);				break;			case SORTBY_USING:				Assert(sortby_opname != NIL);				sortcl->sortop = compatible_oper_opid(sortby_opname,													  restype,													  restype,													  false);				break;			default:				elog(ERROR, "unrecognized sortby_kind: %d", sortby_kind);				break;		}		sortlist = lappend(sortlist, sortcl);	}	return sortlist;}/* * assignSortGroupRef *	  Assign the targetentry an unused ressortgroupref, if it doesn't *	  already have one.  Return the assigned or pre-existing refnumber. * * 'tlist' is the targetlist containing (or to contain) the given targetentry. */IndexassignSortGroupRef(TargetEntry *tle, List *tlist){	Index		maxRef;	List	   *l;	if (tle->resdom->ressortgroupref)	/* already has one? */		return tle->resdom->ressortgroupref;	/* easiest way to pick an unused refnumber: max used + 1 */	maxRef = 0;	foreach(l, tlist)	{		Index		ref = ((TargetEntry *) lfirst(l))->resdom->ressortgroupref;		if (ref > maxRef)			maxRef = ref;	}	tle->resdom->ressortgroupref = maxRef + 1;	return tle->resdom->ressortgroupref;}/* * targetIsInSortList *		Is the given target item already in the sortlist? * * Works for both SortClause and GroupClause lists.  Note that the main * reason we need this routine (and not just a quick test for nonzeroness * of ressortgroupref) is that a TLE might be in only one of the lists. */booltargetIsInSortList(TargetEntry *tle, List *sortList){	Index		ref = tle->resdom->ressortgroupref;	List	   *i;	/* no need to scan list if tle has no marker */	if (ref == 0)		return false;	foreach(i, sortList)	{		SortClause *scl = (SortClause *) lfirst(i);		if (scl->tleSortGroupRef == ref)			return true;	}	return false;}

⌨️ 快捷键说明

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