analyze.c

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

C
2,258
字号
				{					found = true;					break;				}			}			if (found)			{				/* found column in the new table; force it to be NOT NULL */				if (constraint->contype == CONSTR_PRIMARY)					column->is_not_null = TRUE;			}			else if (SystemAttributeByName(key, cxt->hasoids) != NULL)			{				/*				 * column will be a system column in the new table, so				 * accept it.  System columns can't ever be null, so no				 * need to worry about PRIMARY/NOT NULL constraint.				 */				found = true;			}			else if (cxt->inhRelations)			{				/* try inherited tables */				List	   *inher;				foreach(inher, cxt->inhRelations)				{					RangeVar   *inh = lfirst(inher);					Relation	rel;					int			count;					Assert(IsA(inh, RangeVar));					rel = heap_openrv(inh, AccessShareLock);					if (rel->rd_rel->relkind != RELKIND_RELATION)						ereport(ERROR,								(errcode(ERRCODE_WRONG_OBJECT_TYPE),						errmsg("inherited relation \"%s\" is not a table",							   inh->relname)));					for (count = 0; count < rel->rd_att->natts; count++)					{						Form_pg_attribute inhattr = rel->rd_att->attrs[count];						char	   *inhname = NameStr(inhattr->attname);						if (inhattr->attisdropped)							continue;						if (strcmp(key, inhname) == 0)						{							found = true;							/*							 * We currently have no easy way to force an							 * inherited column to be NOT NULL at							 * creation, if its parent wasn't so already.							 * We leave it to DefineIndex to fix things up							 * in this case.							 */							break;						}					}					heap_close(rel, NoLock);					if (found)						break;				}			}			else if (OidIsValid(cxt->relOid))			{				/* ALTER TABLE case: does column already exist? */				HeapTuple	atttuple;				atttuple = SearchSysCacheAttName(cxt->relOid, key);				if (HeapTupleIsValid(atttuple))				{					found = true;					/*					 * If it's not already NOT NULL, leave it to					 * DefineIndex to fix later.					 */					ReleaseSysCache(atttuple);				}			}			if (!found)				ereport(ERROR,						(errcode(ERRCODE_UNDEFINED_COLUMN),					  errmsg("column \"%s\" named in key does not exist",							 key)));			/* Check for PRIMARY KEY(foo, foo) */			foreach(columns, index->indexParams)			{				iparam = (IndexElem *) lfirst(columns);				if (iparam->name && strcmp(key, iparam->name) == 0)				{					if (index->primary)						ereport(ERROR,								(errcode(ERRCODE_DUPLICATE_COLUMN),								 errmsg("column \"%s\" appears twice in primary key constraint",										key)));					else						ereport(ERROR,								(errcode(ERRCODE_DUPLICATE_COLUMN),								 errmsg("column \"%s\" appears twice in unique constraint",										key)));				}			}			/* 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 = makeList1(cxt->pkey);	}	while (indexlist != NIL)	{		index = lfirst(indexlist);		/* if it's pkey, it's already in cxt->alist */		if (index != cxt->pkey)		{			bool		keep = true;			List	   *priorlist;			foreach(priorlist, cxt->alist)			{				IndexStmt  *priorindex = lfirst(priorlist);				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);		}		indexlist = lnext(indexlist);	}	/*	 * Finally, select unique names for all not-previously-named indices,	 * and display NOTICE messages.	 *	 * XXX in ALTER TABLE case, we fail to consider name collisions against	 * pre-existing indexes.	 */	foreach(indexlist, cxt->alist)	{		index = lfirst(indexlist);		if (index->idxname == NULL && index->indexParams != NIL)		{			iparam = (IndexElem *) lfirst(index->indexParams);			/* we should never see an expression item here */			Assert(iparam->expr == NULL);			index->idxname = CreateIndexName(cxt->relation->relname,											 iparam->name,											 "key",											 cxt->alist);		}		if (index->idxname == NULL)		/* should not happen */			elog(ERROR, "failed to make implicit index name");		ereport(NOTICE,				(errmsg("%s / %s%s will create implicit index \"%s\" for table \"%s\"",						cxt->stmtType,			   (strcmp(cxt->stmtType, "ALTER TABLE") == 0) ? "ADD " : "",						(index->primary ? "PRIMARY KEY" : "UNIQUE"),						index->idxname, cxt->relation->relname)));	}}static voidtransformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,					   bool isAddConstraint){	if (cxt->fkconstraints == NIL)		return;	/*	 * For ALTER TABLE ADD CONSTRAINT, nothing to do.  For CREATE TABLE or	 * ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD CONSTRAINT	 * command to execute after the basic command is complete.	 *	 * 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);		List	   *fkclist;		alterstmt->subtype = 'c';		/* preprocessed add constraint */		alterstmt->relation = cxt->relation;		alterstmt->name = NULL;		alterstmt->def = (Node *) cxt->fkconstraints;		/* Don't need to scan the table contents in this case */		foreach(fkclist, cxt->fkconstraints)		{			FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);			fkconstraint->skip_validation = true;		}		cxt->alist = lappend(cxt->alist, (Node *) alterstmt);	}}/* * transformIndexStmt - *	  transforms the qualification of the index statement */static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt){	Query	   *qry;	RangeTblEntry *rte = NULL;	List	   *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 namespace */		addRTEtoQuery(pstate, rte, false, 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 namespace */				addRTEtoQuery(pstate, rte, false, 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;	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.	We don't need to hold a refcount on the relcache	 * entry, however.	 */	heap_close(heap_openrv(stmt->relation, AccessExclusiveLock),			   NoLock);	/*	 * 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 = addRangeTableEntry(pstate, stmt->relation,								makeAlias("*OLD*", NIL),								false, true);	newrte = addRangeTableEntry(pstate, stmt->relation,								makeAlias("*NEW*", NIL),								false, true);	/* Must override addRangeTableEntry's default access-check flags */	oldrte->checkForRead = false;	newrte->checkForRead = false;	/*	 * 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. (Note we marked the RTEs "inFromCl = true" above to allow	 * unqualified references to their fields.)  We do not add them to the	 * joinlist.	 */	switch (stmt->event)	{		case CMD_SELECT:			addRTEtoQuery(pstate, oldrte, false, true);			break;		case CMD_UPDATE:			addRTEtoQuery(pstate, oldrte, false, true);			addRTEtoQuery(pstate, newrte, false, true);			break;		case CMD_INSERT:			addRTEtoQuery(pstate, newrte, false, true);			break;		case CMD_DELETE:			addRTEtoQuery(pstate, oldrte, false, 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 (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 = makeList1(nothing_qry);	}	else	{		List	   *oldactions;		List	   *newactions = NIL;		/*		 * transform each statement, like parse_sub_analyze()		 */		foreach(oldactions, stmt->actions)		{			Node	   *action = (Node *) lfirst(oldactions);			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 marked not inFromCl because we don't want them			 * to be referred to by unqualified field names nor "*" in the			 * rule actions.  We must add them to the namespace, however,			 * or they won't be accessible at all.  We decide later			 * whether to put them in the joinlist.			 */			oldrte = addRangeTableEntry(sub_pstate, stmt->relation,										makeAlias("*OLD*", NIL),										false, false);			newrte = addRangeTableEntry(sub_pstate, stmt->relation,										makeAlias("*NEW*", NIL),										false, false);

⌨️ 快捷键说明

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