analyze.c

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

C
2,258
字号
	Query	   *qry = makeNode(Query);	Node	   *qual;	qry->commandType = CMD_DELETE;	/* set up range table with just the result rel */	qry->resultRelation = setTargetTable(pstate, stmt->relation,							  interpretInhOption(stmt->relation->inhOpt),										 true);	qry->distinctClause = NIL;	/* fix where clause */	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");	/* done building the range table and jointree */	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)		parseCheckAggregates(pstate, qry);	return qry;}/* * transformInsertStmt - *	  transform an Insert Statement */static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,					List **extras_before, List **extras_after){	Query	   *qry = makeNode(Query);	List	   *sub_rtable;	List	   *sub_namespace;	List	   *icolumns;	List	   *attrnos;	List	   *attnos;	List	   *tl;	qry->commandType = CMD_INSERT;	pstate->p_is_insert = true;	/*	 * If a non-nil rangetable/namespace was passed in, and we are doing	 * INSERT/SELECT, arrange to pass the rangetable/namespace down to the	 * SELECT.	This can only happen if we are inside a CREATE RULE, and	 * in that case we want the rule's OLD and NEW rtable entries to	 * appear as part of the SELECT's rtable, not as outer references for	 * it.	(Kluge!)  The SELECT's joinlist is not affected however. We	 * must do this before adding the target table to the INSERT's rtable.	 */	if (stmt->selectStmt)	{		sub_rtable = pstate->p_rtable;		pstate->p_rtable = NIL;		sub_namespace = pstate->p_namespace;		pstate->p_namespace = NIL;	}	else	{		sub_rtable = NIL;		/* not used, but keep compiler quiet */		sub_namespace = NIL;	}	/*	 * Must get write lock on INSERT target table before scanning SELECT,	 * else we will grab the wrong kind of initial lock if the target	 * table is also mentioned in the SELECT part.	Note that the target	 * table is not added to the joinlist or namespace.	 */	qry->resultRelation = setTargetTable(pstate, stmt->relation,										 false, false);	/*	 * Is it INSERT ... SELECT or INSERT ... VALUES?	 */	if (stmt->selectStmt)	{		/*		 * We make the sub-pstate a child of the outer pstate so that it		 * can see any Param definitions supplied from above.  Since the		 * outer pstate's rtable and namespace are presently empty, there		 * are no side-effects of exposing names the sub-SELECT shouldn't		 * be able to see.		 */		ParseState *sub_pstate = make_parsestate(pstate);		Query	   *selectQuery;		RangeTblEntry *rte;		RangeTblRef *rtr;		/*		 * Process the source SELECT.		 *		 * It is important that this be handled just like a standalone		 * SELECT; otherwise the behavior of SELECT within INSERT might be		 * different from a stand-alone SELECT. (Indeed, Postgres up		 * through 6.5 had bugs of just that nature...)		 */		sub_pstate->p_rtable = sub_rtable;		sub_pstate->p_namespace = sub_namespace;		/*		 * Note: we are not expecting that extras_before and extras_after		 * are going to be used by the transformation of the SELECT		 * statement.		 */		selectQuery = transformStmt(sub_pstate, stmt->selectStmt,									extras_before, extras_after);		release_pstate_resources(sub_pstate);		pfree(sub_pstate);		Assert(IsA(selectQuery, Query));		Assert(selectQuery->commandType == CMD_SELECT);		if (selectQuery->into)			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("INSERT ... SELECT may not specify INTO")));		/*		 * Make the source be a subquery in the INSERT's rangetable, and		 * add it to the INSERT's joinlist.		 */		rte = addRangeTableEntryForSubquery(pstate,											selectQuery,											makeAlias("*SELECT*", NIL),											true);		rtr = makeNode(RangeTblRef);		/* assume new rte is at end */		rtr->rtindex = length(pstate->p_rtable);		Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));		pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);		/*		 * Generate a targetlist for the INSERT that selects all the		 * non-resjunk columns from the subquery.  (We need this to be		 * separate from the subquery's tlist because we may add columns,		 * insert datatype coercions, etc.)		 *		 * HACK: unknown-type constants and params in the INSERT's targetlist		 * are copied up as-is rather than being referenced as subquery		 * outputs.  This is to ensure that when we try to coerce them to		 * the target column's datatype, the right things happen (see		 * special cases in coerce_type).  Otherwise, this fails: INSERT		 * INTO foo SELECT 'bar', ... FROM baz		 */		qry->targetList = NIL;		foreach(tl, selectQuery->targetList)		{			TargetEntry *tle = (TargetEntry *) lfirst(tl);			Resdom	   *resnode = tle->resdom;			Expr	   *expr;			if (resnode->resjunk)				continue;			if (tle->expr &&				(IsA(tle->expr, Const) ||IsA(tle->expr, Param)) &&				exprType((Node *) tle->expr) == UNKNOWNOID)				expr = tle->expr;			else				expr = (Expr *) makeVar(rtr->rtindex,										resnode->resno,										resnode->restype,										resnode->restypmod,										0);			resnode = copyObject(resnode);			resnode->resno = (AttrNumber) pstate->p_next_resno++;			qry->targetList = lappend(qry->targetList,									  makeTargetEntry(resnode, expr));		}	}	else	{		/*		 * For INSERT ... VALUES, transform the given list of values to		 * form a targetlist for the INSERT.		 */		qry->targetList = transformTargetList(pstate, stmt->targetList);	}	/*	 * Now we are done with SELECT-like processing, and can get on with	 * transforming the target list to match the INSERT target columns.	 */	/* Prepare to assign non-conflicting resnos to resjunk attributes */	if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)		pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;	/* Validate stmt->cols list, or build default list if no list given */	icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);	/*	 * Prepare columns for assignment to target table.	 */	attnos = attrnos;	/* cannot use foreach here because of possible lremove */	tl = qry->targetList;	while (tl)	{		TargetEntry *tle = (TargetEntry *) lfirst(tl);		ResTarget  *col;		/* must advance tl before lremove possibly pfree's it */		tl = lnext(tl);		if (icolumns == NIL || attnos == NIL)			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),			 errmsg("INSERT has more expressions than target columns")));		col = (ResTarget *) lfirst(icolumns);		Assert(IsA(col, ResTarget));		Assert(!tle->resdom->resjunk);		updateTargetListEntry(pstate, tle, col->name, lfirsti(attnos),							  col->indirection);		icolumns = lnext(icolumns);		attnos = lnext(attnos);	}	/*	 * Ensure that the targetlist has the same number of entries that were	 * present in the columns list.  Don't do the check unless an explicit	 * columns list was given, though. statements.	 */	if (stmt->cols != NIL && (icolumns != NIL || attnos != NIL))		ereport(ERROR,				(errcode(ERRCODE_SYNTAX_ERROR),			 errmsg("INSERT has more target columns than expressions")));	/* done building the range table and jointree */	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)		parseCheckAggregates(pstate, qry);	return qry;}/* *	makeObjectName() * *	Create a name for an implicitly created index, sequence, constraint, etc. * *	The parameters are: the original table name, the original field name, and *	a "type" string (such as "seq" or "pkey").	The field name and/or type *	can be NULL if not relevant. * *	The result is a palloc'd string. * *	The basic result we want is "name1_name2_type", omitting "_name2" or *	"_type" when those parameters are NULL.  However, we must generate *	a name with less than NAMEDATALEN characters!  So, we truncate one or *	both names if necessary to make a short-enough string.	The type part *	is never truncated (so it had better be reasonably short). * *	To reduce the probability of collisions, we might someday add more *	smarts to this routine, like including some "hash" characters computed *	from the truncated characters.	Currently it seems best to keep it simple, *	so that the generated names are easily predictable by a person. */char *makeObjectName(char *name1, char *name2, char *typename){	char	   *name;	int			overhead = 0;	/* chars needed for type and underscores */	int			availchars;		/* chars available for name(s) */	int			name1chars;		/* chars allocated to name1 */	int			name2chars;		/* chars allocated to name2 */	int			ndx;	name1chars = strlen(name1);	if (name2)	{		name2chars = strlen(name2);		overhead++;				/* allow for separating underscore */	}	else		name2chars = 0;	if (typename)		overhead += strlen(typename) + 1;	availchars = NAMEDATALEN - 1 - overhead;	/*	 * If we must truncate,  preferentially truncate the longer name. This	 * logic could be expressed without a loop, but it's simple and	 * obvious as a loop.	 */	while (name1chars + name2chars > availchars)	{		if (name1chars > name2chars)			name1chars--;		else			name2chars--;	}	if (name1)		name1chars = pg_mbcliplen(name1, name1chars, name1chars);	if (name2)		name2chars = pg_mbcliplen(name2, name2chars, name2chars);	/* Now construct the string using the chosen lengths */	name = palloc(name1chars + name2chars + overhead + 1);	strncpy(name, name1, name1chars);	ndx = name1chars;	if (name2)	{		name[ndx++] = '_';		strncpy(name + ndx, name2, name2chars);		ndx += name2chars;	}	if (typename)	{		name[ndx++] = '_';		strcpy(name + ndx, typename);	}	else		name[ndx] = '\0';	return name;}static char *CreateIndexName(char *table_name, char *column_name,				char *label, List *indices){	int			pass = 0;	char	   *iname = NULL;	List	   *ilist;	char		typename[NAMEDATALEN];	/*	 * The type name for makeObjectName is label, or labelN if that's	 * necessary to prevent collisions among multiple indexes for the same	 * table.  Note there is no check for collisions with already-existing	 * indexes, only among the indexes we're about to create now; this	 * ought to be improved someday.	 */	strncpy(typename, label, sizeof(typename));	for (;;)	{		iname = makeObjectName(table_name, column_name, typename);		foreach(ilist, indices)		{			IndexStmt  *index = lfirst(ilist);			if (index->idxname != NULL &&				strcmp(iname, index->idxname) == 0)				break;		}		/* ran through entire list? then no name conflict found so done */		if (ilist == NIL)			break;		/* found a conflict, so try a new name component */		pfree(iname);		snprintf(typename, sizeof(typename), "%s%d", label, ++pass);	}	return iname;}/* * transformCreateStmt - *	  transforms the "create table" statement *	  SQL92 allows constraints to be scattered all over, so thumb through *	   the columns and collect all constraints into one place. *	  If there are any implied indices (e.g. UNIQUE or PRIMARY KEY) *	   then expand those into multiple IndexStmt blocks. *	  - thomas 1997-12-02 */static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt,					List **extras_before, List **extras_after){	CreateStmtContext cxt;	Query	   *q;	List	   *elements;	cxt.stmtType = "CREATE TABLE";	cxt.relation = stmt->relation;	cxt.inhRelations = stmt->inhRelations;	cxt.hasoids = stmt->hasoids;	cxt.relOid = InvalidOid;	cxt.columns = NIL;	cxt.ckconstraints = NIL;	cxt.fkconstraints = NIL;	cxt.ixconstraints = NIL;	cxt.blist = NIL;	cxt.alist = NIL;	cxt.pkey = NULL;	/*	 * Run through each primary element in the table creation clause.	 * Separate column defs from constraints, and do preliminary analysis.	 */	foreach(elements, stmt->tableElts)	{		Node	   *element = lfirst(elements);		switch (nodeTag(element))		{			case T_ColumnDef:				transformColumnDefinition(pstate, &cxt,										  (ColumnDef *) element);				break;			case T_Constraint:				transformTableConstraint(pstate, &cxt,										 (Constraint *) element);				break;			case T_FkConstraint:				/* No pre-transformation needed */				cxt.fkconstraints = lappend(cxt.fkconstraints, element);				break;			case T_InhRelation:				transformInhRelation(pstate, &cxt,									 (InhRelation *) element);				break;			default:				elog(ERROR, "unrecognized node type: %d",					 (int) nodeTag(element));				break;		}	}	Assert(stmt->constraints == NIL);	/*	 * Postprocess constraints that give rise to index definitions.	 */	transformIndexConstraints(pstate, &cxt);	/*	 * Postprocess foreign-key constraints.	 */	transformFKConstraints(pstate, &cxt, false);

⌨️ 快捷键说明

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