analyze.c

来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,222 行 · 第 1/5 页

C
2,222
字号
				}			}			/* OK, add it to the index definition */			iparam = makeNode(IndexElem);			iparam->name = pstrdup(key);			iparam->expr = NULL;			iparam->opclass = NIL;			index->indexParams = lappend(index->indexParams, iparam);		}		indexlist = lappend(indexlist, index);	}	/*	 * Scan the index list and remove any redundant index specifications. This	 * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A	 * strict reading of SQL92 would suggest raising an error instead, but	 * that strikes me as too anal-retentive. - tgl 2001-02-14	 *	 * XXX in ALTER TABLE case, it'd be nice to look for duplicate	 * pre-existing indexes, too.	 */	cxt->alist = NIL;	if (cxt->pkey != NULL)	{		/* Make sure we keep the PKEY index in preference to others... */		cxt->alist = list_make1(cxt->pkey);	}	foreach(l, indexlist)	{		bool		keep = true;		ListCell   *k;		index = lfirst(l);		/* if it's pkey, it's already in cxt->alist */		if (index == cxt->pkey)			continue;		foreach(k, cxt->alist)		{			IndexStmt  *priorindex = lfirst(k);			if (equal(index->indexParams, priorindex->indexParams))			{				/*				 * If the prior index is as yet unnamed, and this one is				 * named, then transfer the name to the prior index. This				 * ensures that if we have named and unnamed constraints,				 * we'll use (at least one of) the names for the index.				 */				if (priorindex->idxname == NULL)					priorindex->idxname = index->idxname;				keep = false;				break;			}		}		if (keep)			cxt->alist = lappend(cxt->alist, index);	}}static voidtransformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,					   bool skipValidation, bool isAddConstraint){	ListCell   *fkclist;	if (cxt->fkconstraints == NIL)		return;	/*	 * If CREATE TABLE or adding a column with NULL default, we can safely	 * skip validation of the constraint.	 */	if (skipValidation)	{		foreach(fkclist, cxt->fkconstraints)		{			FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);			fkconstraint->skip_validation = true;		}	}	/*	 * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD	 * CONSTRAINT command to execute after the basic command is complete. (If	 * called from ADD CONSTRAINT, that routine will add the FK constraints to	 * its own subcommand list.)	 *	 * Note: the ADD CONSTRAINT command must also execute after any index	 * creation commands.  Thus, this should run after	 * transformIndexConstraints, so that the CREATE INDEX commands are	 * already in cxt->alist.	 */	if (!isAddConstraint)	{		AlterTableStmt *alterstmt = makeNode(AlterTableStmt);		alterstmt->relation = cxt->relation;		alterstmt->cmds = NIL;		alterstmt->relkind = OBJECT_TABLE;		foreach(fkclist, cxt->fkconstraints)		{			FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);			AlterTableCmd *altercmd = makeNode(AlterTableCmd);			altercmd->subtype = AT_ProcessedConstraint;			altercmd->name = NULL;			altercmd->def = (Node *) fkconstraint;			alterstmt->cmds = lappend(alterstmt->cmds, altercmd);		}		cxt->alist = lappend(cxt->alist, alterstmt);	}}/* * transformIndexStmt - *	  transforms the qualification of the index statement */static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt){	Query	   *qry;	RangeTblEntry *rte = NULL;	ListCell   *l;	qry = makeNode(Query);	qry->commandType = CMD_UTILITY;	/* take care of the where clause */	if (stmt->whereClause)	{		/*		 * Put the parent table into the rtable so that the WHERE clause can		 * refer to its fields without qualification.  Note that this only		 * works if the parent table already exists --- so we can't easily		 * support predicates on indexes created implicitly by CREATE TABLE.		 * Fortunately, that's not necessary.		 */		rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);		/* no to join list, yes to namespaces */		addRTEtoQuery(pstate, rte, false, true, true);		stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,												 "WHERE");	}	/* take care of any index expressions */	foreach(l, stmt->indexParams)	{		IndexElem  *ielem = (IndexElem *) lfirst(l);		if (ielem->expr)		{			/* Set up rtable as for predicate, see notes above */			if (rte == NULL)			{				rte = addRangeTableEntry(pstate, stmt->relation, NULL,										 false, true);				/* no to join list, yes to namespaces */				addRTEtoQuery(pstate, rte, false, true, true);			}			ielem->expr = transformExpr(pstate, ielem->expr);			/*			 * We check only that the result type is legitimate; this is for			 * consistency with what transformWhereClause() checks for the			 * predicate.  DefineIndex() will make more checks.			 */			if (expression_returns_set(ielem->expr))				ereport(ERROR,						(errcode(ERRCODE_DATATYPE_MISMATCH),						 errmsg("index expression may not return a set")));		}	}	qry->hasSubLinks = pstate->p_hasSubLinks;	stmt->rangetable = pstate->p_rtable;	qry->utilityStmt = (Node *) stmt;	return qry;}/* * transformRuleStmt - *	  transform a Create Rule Statement. The actions is a list of parse *	  trees which is transformed into a list of query trees. */static Query *transformRuleStmt(ParseState *pstate, RuleStmt *stmt,				  List **extras_before, List **extras_after){	Query	   *qry;	Relation	rel;	RangeTblEntry *oldrte;	RangeTblEntry *newrte;	qry = makeNode(Query);	qry->commandType = CMD_UTILITY;	qry->utilityStmt = (Node *) stmt;	/*	 * To avoid deadlock, make sure the first thing we do is grab	 * AccessExclusiveLock on the target relation.	This will be needed by	 * DefineQueryRewrite(), and we don't want to grab a lesser lock	 * beforehand.	 */	rel = heap_openrv(stmt->relation, AccessExclusiveLock);	/*	 * NOTE: 'OLD' must always have a varno equal to 1 and 'NEW' equal to 2.	 * Set up their RTEs in the main pstate for use in parsing the rule	 * qualification.	 */	Assert(pstate->p_rtable == NIL);	oldrte = addRangeTableEntryForRelation(pstate, rel,										   makeAlias("*OLD*", NIL),										   false, false);	newrte = addRangeTableEntryForRelation(pstate, rel,										   makeAlias("*NEW*", NIL),										   false, false);	/* Must override addRangeTableEntry's default access-check flags */	oldrte->requiredPerms = 0;	newrte->requiredPerms = 0;	/*	 * They must be in the namespace too for lookup purposes, but only add the	 * one(s) that are relevant for the current kind of rule.  In an UPDATE	 * rule, quals must refer to OLD.field or NEW.field to be unambiguous, but	 * there's no need to be so picky for INSERT & DELETE.  We do not add them	 * to the joinlist.	 */	switch (stmt->event)	{		case CMD_SELECT:			addRTEtoQuery(pstate, oldrte, false, true, true);			break;		case CMD_UPDATE:			addRTEtoQuery(pstate, oldrte, false, true, true);			addRTEtoQuery(pstate, newrte, false, true, true);			break;		case CMD_INSERT:			addRTEtoQuery(pstate, newrte, false, true, true);			break;		case CMD_DELETE:			addRTEtoQuery(pstate, oldrte, false, true, true);			break;		default:			elog(ERROR, "unrecognized event type: %d",				 (int) stmt->event);			break;	}	/* take care of the where clause */	stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,											 "WHERE");	if (list_length(pstate->p_rtable) != 2)		/* naughty, naughty... */		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),				 errmsg("rule WHERE condition may not contain references to other relations")));	/* aggregates not allowed (but subselects are okay) */	if (pstate->p_hasAggs)		ereport(ERROR,				(errcode(ERRCODE_GROUPING_ERROR),		errmsg("rule WHERE condition may not contain aggregate functions")));	/* save info about sublinks in where clause */	qry->hasSubLinks = pstate->p_hasSubLinks;	/*	 * 'instead nothing' rules with a qualification need a query rangetable so	 * the rewrite handler can add the negated rule qualification to the	 * original query. We create a query with the new command type CMD_NOTHING	 * here that is treated specially by the rewrite system.	 */	if (stmt->actions == NIL)	{		Query	   *nothing_qry = makeNode(Query);		nothing_qry->commandType = CMD_NOTHING;		nothing_qry->rtable = pstate->p_rtable;		nothing_qry->jointree = makeFromExpr(NIL, NULL);		/* no join wanted */		stmt->actions = list_make1(nothing_qry);	}	else	{		ListCell   *l;		List	   *newactions = NIL;		/*		 * transform each statement, like parse_sub_analyze()		 */		foreach(l, stmt->actions)		{			Node	   *action = (Node *) lfirst(l);			ParseState *sub_pstate = make_parsestate(pstate->parentParseState);			Query	   *sub_qry,					   *top_subqry;			bool		has_old,						has_new;			/*			 * Set up OLD/NEW in the rtable for this statement.  The entries			 * are added only to relnamespace, not varnamespace, because we			 * don't want them to be referred to by unqualified field names			 * nor "*" in the rule actions.  We decide later whether to put			 * them in the joinlist.			 */			oldrte = addRangeTableEntryForRelation(sub_pstate, rel,												   makeAlias("*OLD*", NIL),												   false, false);			newrte = addRangeTableEntryForRelation(sub_pstate, rel,												   makeAlias("*NEW*", NIL),												   false, false);			oldrte->requiredPerms = 0;			newrte->requiredPerms = 0;			addRTEtoQuery(sub_pstate, oldrte, false, true, false);			addRTEtoQuery(sub_pstate, newrte, false, true, false);			/* 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, false);				sub_qry->jointree->fromlist = sub_pstate->p_joinlist;			}			newactions = lappend(newactions, top_subqry);			release_pstate_resources(sub_pstate);			pfree(sub_pstate);		}

⌨️ 快捷键说明

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