analyze.c

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

C
2,258
字号
			oldrte->checkForRead = false;			newrte->checkForRead = false;			addRTEtoQuery(sub_pstate, oldrte, false, true);			addRTEtoQuery(sub_pstate, newrte, false, true);			/* Transform the rule action statement */			top_subqry = transformStmt(sub_pstate, action,									   extras_before, extras_after);			/*			 * We cannot support utility-statement actions (eg NOTIFY)			 * with nonempty rule WHERE conditions, because there's no way			 * to make the utility action execute conditionally.			 */			if (top_subqry->commandType == CMD_UTILITY &&				stmt->whereClause != NULL)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("rules with WHERE conditions may only have SELECT, INSERT, UPDATE, or DELETE actions")));			/*			 * If the action is INSERT...SELECT, OLD/NEW have been pushed			 * down into the SELECT, and that's what we need to look at.			 * (Ugly kluge ... try to fix this when we redesign			 * querytrees.)			 */			sub_qry = getInsertSelectQuery(top_subqry, NULL);			/*			 * If the sub_qry is a setop, we cannot attach any			 * qualifications to it, because the planner won't notice			 * them.  This could perhaps be relaxed someday, but for now,			 * we may as well reject such a rule immediately.			 */			if (sub_qry->setOperations != NULL && stmt->whereClause != NULL)				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));			/*			 * Validate action's use of OLD/NEW, qual too			 */			has_old =				rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||				rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);			has_new =				rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||				rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);			switch (stmt->event)			{				case CMD_SELECT:					if (has_old)						ereport(ERROR,							 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							  errmsg("ON SELECT rule may not use OLD")));					if (has_new)						ereport(ERROR,							 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							  errmsg("ON SELECT rule may not use NEW")));					break;				case CMD_UPDATE:					/* both are OK */					break;				case CMD_INSERT:					if (has_old)						ereport(ERROR,							 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							  errmsg("ON INSERT rule may not use OLD")));					break;				case CMD_DELETE:					if (has_new)						ereport(ERROR,							 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							  errmsg("ON DELETE rule may not use NEW")));					break;				default:					elog(ERROR, "unrecognized event type: %d",						 (int) stmt->event);					break;			}			/*			 * For efficiency's sake, add OLD to the rule action's			 * jointree only if it was actually referenced in the			 * statement or qual.			 *			 * For INSERT, NEW is not really a relation (only a reference to			 * the to-be-inserted tuple) and should never be added to the			 * jointree.			 *			 * For UPDATE, we treat NEW as being another kind of reference to			 * OLD, because it represents references to *transformed*			 * tuples of the existing relation.  It would be wrong to			 * enter NEW separately in the jointree, since that would			 * cause a double join of the updated relation.  It's also			 * wrong to fail to make a jointree entry if only NEW and not			 * OLD is mentioned.			 */			if (has_old || (has_new && stmt->event == CMD_UPDATE))			{				/*				 * If sub_qry is a setop, manipulating its jointree will				 * do no good at all, because the jointree is dummy. (This				 * should be a can't-happen case because of prior tests.)				 */				if (sub_qry->setOperations != NULL)					ereport(ERROR,							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),							 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));				/* hack so we can use addRTEtoQuery() */				sub_pstate->p_rtable = sub_qry->rtable;				sub_pstate->p_joinlist = sub_qry->jointree->fromlist;				addRTEtoQuery(sub_pstate, oldrte, true, false);				sub_qry->jointree->fromlist = sub_pstate->p_joinlist;			}			newactions = lappend(newactions, top_subqry);			release_pstate_resources(sub_pstate);			pfree(sub_pstate);		}		stmt->actions = newactions;	}	return qry;}/* * transformSelectStmt - *	  transforms a Select Statement * * Note: this is also used for DECLARE CURSOR statements. */static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt){	Query	   *qry = makeNode(Query);	Node	   *qual;	qry->commandType = CMD_SELECT;	/* make FOR UPDATE clause available to addRangeTableEntry */	pstate->p_forUpdate = stmt->forUpdate;	/* process the FROM clause */	transformFromClause(pstate, stmt->fromClause);	/* transform targetlist */	qry->targetList = transformTargetList(pstate, stmt->targetList);	/* handle any SELECT INTO/CREATE TABLE AS spec */	qry->into = stmt->into;	if (stmt->intoColNames)		applyColumnNames(qry->targetList, stmt->intoColNames);	/* mark column origins */	markTargetListOrigins(pstate, qry->targetList);	/* transform WHERE */	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");	/*	 * Initial processing of HAVING clause is just like WHERE clause.	 * Additional work will be done in optimizer/plan/planner.c.	 */	qry->havingQual = transformWhereClause(pstate, stmt->havingClause,										   "HAVING");	/*	 * Transform sorting/grouping stuff.  Do ORDER BY first because both	 * transformGroupClause and transformDistinctClause need the results.	 */	qry->sortClause = transformSortClause(pstate,										  stmt->sortClause,										  qry->targetList,										  true /* fix unknowns */ );	qry->groupClause = transformGroupClause(pstate,											stmt->groupClause,											qry->targetList,											qry->sortClause);	qry->distinctClause = transformDistinctClause(pstate,												  stmt->distinctClause,												  qry->targetList,												  &qry->sortClause);	qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,											"OFFSET");	qry->limitCount = transformLimitClause(pstate, stmt->limitCount,										   "LIMIT");	qry->rtable = pstate->p_rtable;	qry->jointree = makeFromExpr(pstate->p_joinlist, qual);	qry->hasSubLinks = pstate->p_hasSubLinks;	qry->hasAggs = pstate->p_hasAggs;	if (pstate->p_hasAggs || qry->groupClause)		parseCheckAggregates(pstate, qry);	if (stmt->forUpdate != NIL)		transformForUpdate(qry, stmt->forUpdate);	return qry;}/* * transformSetOperationsStmt - *	  transforms a set-operations tree * * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT * structure to it.  We must transform each leaf SELECT and build up a top- * level Query that contains the leaf SELECTs as subqueries in its rangetable. * The tree of set operations is converted into the setOperations field of * the top-level Query. */static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt){	Query	   *qry = makeNode(Query);	SelectStmt *leftmostSelect;	int			leftmostRTI;	Query	   *leftmostQuery;	SetOperationStmt *sostmt;	RangeVar   *into;	List	   *intoColNames;	List	   *sortClause;	Node	   *limitOffset;	Node	   *limitCount;	List	   *forUpdate;	Node	   *node;	List	   *lefttl,			   *dtlist,			   *targetvars,			   *targetnames,			   *sv_namespace,			   *sv_rtable;	RangeTblEntry *jrte;	RangeTblRef *jrtr;	int			tllen;	qry->commandType = CMD_SELECT;	/*	 * Find leftmost leaf SelectStmt; extract the one-time-only items from	 * it and from the top-level node.	 */	leftmostSelect = stmt->larg;	while (leftmostSelect && leftmostSelect->op != SETOP_NONE)		leftmostSelect = leftmostSelect->larg;	Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&		   leftmostSelect->larg == NULL);	into = leftmostSelect->into;	intoColNames = leftmostSelect->intoColNames;	/* clear them to prevent complaints in transformSetOperationTree() */	leftmostSelect->into = NULL;	leftmostSelect->intoColNames = NIL;	/*	 * These are not one-time, exactly, but we want to process them here	 * and not let transformSetOperationTree() see them --- else it'll	 * just recurse right back here!	 */	sortClause = stmt->sortClause;	limitOffset = stmt->limitOffset;	limitCount = stmt->limitCount;	forUpdate = stmt->forUpdate;	stmt->sortClause = NIL;	stmt->limitOffset = NULL;	stmt->limitCount = NULL;	stmt->forUpdate = NIL;	/* We don't support forUpdate with set ops at the moment. */	if (forUpdate)		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));	/*	 * Recursively transform the components of the tree.	 */	sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt);	Assert(sostmt && IsA(sostmt, SetOperationStmt));	qry->setOperations = (Node *) sostmt;	/*	 * Re-find leftmost SELECT (now it's a sub-query in rangetable)	 */	node = sostmt->larg;	while (node && IsA(node, SetOperationStmt))		node = ((SetOperationStmt *) node)->larg;	Assert(node && IsA(node, RangeTblRef));	leftmostRTI = ((RangeTblRef *) node)->rtindex;	leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;	Assert(leftmostQuery != NULL);	/*	 * Generate dummy targetlist for outer query using column names of	 * leftmost select and common datatypes of topmost set operation. Also	 * make lists of the dummy vars and their names for use in parsing	 * ORDER BY.	 *	 * Note: we use leftmostRTI as the varno of the dummy variables. It	 * shouldn't matter too much which RT index they have, as long as they	 * have one that corresponds to a real RT entry; else funny things may	 * happen when the tree is mashed by rule rewriting.	 */	qry->targetList = NIL;	targetvars = NIL;	targetnames = NIL;	lefttl = leftmostQuery->targetList;	foreach(dtlist, sostmt->colTypes)	{		Oid			colType = lfirsto(dtlist);		Resdom	   *leftResdom = ((TargetEntry *) lfirst(lefttl))->resdom;		char	   *colName;		Resdom	   *resdom;		Expr	   *expr;		Assert(!leftResdom->resjunk);		colName = pstrdup(leftResdom->resname);		resdom = makeResdom((AttrNumber) pstate->p_next_resno++,							colType,							-1,							colName,							false);		expr = (Expr *) makeVar(leftmostRTI,								leftResdom->resno,								colType,								-1,								0);		qry->targetList = lappend(qry->targetList,								  makeTargetEntry(resdom, expr));		targetvars = lappend(targetvars, expr);		targetnames = lappend(targetnames, makeString(colName));		lefttl = lnext(lefttl);	}	/*	 * Handle SELECT INTO/CREATE TABLE AS.	 *	 * Any column names from CREATE TABLE AS need to be attached to both the	 * top level and the leftmost subquery.  We do not do this earlier	 * because we do *not* want the targetnames list to be affected.	 */	qry->into = into;	if (intoColNames)	{		applyColumnNames(qry->targetList, intoColNames);		applyColumnNames(leftmostQuery->targetList, intoColNames);	}	/*	 * As a first step towards supporting sort clauses that are	 * expressions using the output columns, generate a namespace entry	 * that makes the output columns visible.  A Join RTE node is handy	 * for this, since we can easily control the Vars generated upon	 * matches.	 *	 * Note: we don't yet do anything useful with such cases, but at least	 * "ORDER BY upper(foo)" will draw the right error message rather than	 * "foo not found".	 */	jrte = addRangeTableEntryForJoin(NULL,									 targetnames,									 JOIN_INNER,									 targetvars,									 NULL,									 true);	jrtr = makeNode(RangeTblRef);	jrtr->rtindex = 1;			/* only entry in dummy rtable */	sv_rtable = pstate->p_rtable;	pstate->p_rtable = makeList1(jrte);	sv_namespace = pstate->p_namespace;	pstate->p_namespace = makeList1(jrtr);	/*	 * For now, we don't support resjunk sort clauses on the output of a	 * setOperation tree --- you can only use the SQL92-spec options of	 * selecting an output column by name or number.  Enforce by checking	 * that transformSortClause doesn't add any items to tlist.	 */	tllen = length(qry->targetList);	qry->sortClause = transformSortClause(pstate,										  sortClause,										  qry->targetList,									  false /* no unknowns expected */ );	pstate->p_namespace = sv_namespace;	pstate->p_rtable = sv_rtable;	if (tllen != length(qry->targetList))		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns")));	qry->limitOffset = transformLimitClause(pstate, limitOffset,											"OFFSET");	qry->limitCount = transformLimitClause(pstate, limitCount,										   "LIMIT");	qry->rtable = pstate->p_rtable;	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);	qry->hasSubLinks = pstate->p_hasSubLinks;	qry->hasAggs = pstate->p_hasAggs;	if (pstate->p_hasAggs || qry->groupClause)		parseCheckAggregates(pstate, qry);	if (forUpdate != NIL)		transformForUpdate(qry, forUpdate);	return qry;}/* * transformSetOperationTree *		Recursively transform leaves and internal nodes of a set-op tree */static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt){	bool		isLeaf;	Assert(stmt && IsA(stmt, SelectStmt));	/*	 * Validity-check both leaf and internal SELECTs for disallowed ops.	 */	if (stmt->into)		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));	/* We don't support forUpdate with set ops at the moment. */	if (stmt->forUpdate)		ereport(ERROR,				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),				 errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT")));	/*	 * If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT	 * clauses attached, w

⌨️ 快捷键说明

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