analyze.c

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

C
2,222
字号
		}		if (alist_item != NULL)			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("CREATE VIEW specifies more column "							"names than columns")));	}	return result;}/* * transformDeleteStmt - *	  transforms a Delete Statement */static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt){	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,										 ACL_DELETE);	qry->distinctClause = NIL;	/*	 * The USING clause is non-standard SQL syntax, and is equivalent in	 * functionality to the FROM list that can be specified for UPDATE. The	 * USING keyword is used rather than FROM because FROM is already a	 * keyword in the DELETE syntax.	 */	transformFromClause(pstate, stmt->usingClause);	/* 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);	Query	   *selectQuery = NULL;	List	   *sub_rtable;	List	   *sub_relnamespace;	List	   *sub_varnamespace;	List	   *icolumns;	List	   *attrnos;	ListCell   *icols;	ListCell   *attnos;	ListCell   *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_relnamespace = pstate->p_relnamespace;		pstate->p_relnamespace = NIL;		sub_varnamespace = pstate->p_varnamespace;		pstate->p_varnamespace = NIL;	}	else	{		sub_rtable = NIL;		/* not used, but keep compiler quiet */		sub_relnamespace = NIL;		sub_varnamespace = 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, ACL_INSERT);	/*	 * 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);		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_relnamespace = sub_relnamespace;		sub_pstate->p_varnamespace = sub_varnamespace;		/*		 * 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),											false);		rtr = makeNode(RangeTblRef);		/* assume new rte is at end */		rtr->rtindex = list_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 SELECT'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);			Expr	   *expr;			if (tle->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,										tle->resno,										exprType((Node *) tle->expr),										exprTypmod((Node *) tle->expr),										0);			tle = makeTargetEntry(expr,								  (AttrNumber) pstate->p_next_resno++,								  tle->resname,								  false);			qry->targetList = lappend(qry->targetList, tle);		}	}	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.	 */	icols = list_head(icolumns);	attnos = list_head(attrnos);	foreach(tl, qry->targetList)	{		TargetEntry *tle = (TargetEntry *) lfirst(tl);		ResTarget  *col;		if (icols == NULL || attnos == NULL)			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),				 errmsg("INSERT has more expressions than target columns")));		col = (ResTarget *) lfirst(icols);		Assert(IsA(col, ResTarget));		Assert(!tle->resjunk);		updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos),							  col->indirection);		icols = lnext(icols);		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.	 */	if (stmt->cols != NIL && (icols != NULL || attnos != NULL))		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;}/* * 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;	ListCell   *elements;	cxt.stmtType = "CREATE TABLE";	cxt.relation = stmt->relation;	cxt.inhRelations = stmt->inhRelations;	cxt.isalter = false;	cxt.columns = NIL;	cxt.ckconstraints = NIL;	cxt.fkconstraints = NIL;	cxt.ixconstraints = NIL;	cxt.blist = NIL;	cxt.alist = NIL;	cxt.pkey = NULL;	cxt.hasoids = interpretOidsOption(stmt->hasoids);	/*	 * 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, true, false);	/*	 * Output results.	 */	q = makeNode(Query);	q->commandType = CMD_UTILITY;	q->utilityStmt = (Node *) stmt;	stmt->tableElts = cxt.columns;	stmt->constraints = cxt.ckconstraints;	*extras_before = list_concat(*extras_before, cxt.blist);	*extras_after = list_concat(cxt.alist, *extras_after);	return q;}static voidtransformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,						  ColumnDef *column){	bool		is_serial;	bool		saw_nullable;	Constraint *constraint;	ListCell   *clist;	cxt->columns = lappend(cxt->columns, column);	/* Check for SERIAL pseudo-types */	is_serial = false;	if (list_length(column->typename->names) == 1)	{		char	   *typname = strVal(linitial(column->typename->names));		if (strcmp(typname, "serial") == 0 ||			strcmp(typname, "serial4") == 0)		{			is_serial = true;			column->typename->names = NIL;			column->typename->typeid = INT4OID;		}		else if (strcmp(typname, "bigserial") == 0 ||				 strcmp(typname, "serial8") == 0)		{			is_serial = true;			column->typename->names = NIL;			column->typename->typeid = INT8OID;		}	}	/* Do necessary work on the column type declaration */	transformColumnType(pstate, column);	/* Special actions for SERIAL pseudo-types */	if (is_serial)	{		Oid			snamespaceid;		char	   *snamespace;		char	   *sname;		char	   *qstring;		A_Const    *snamenode;		FuncCall   *funccallnode;		CreateSeqStmt *seqstmt;		/*		 * Determine namespace and name to use for the sequence.		 *		 * Although we use ChooseRelationName, it's not guaranteed that the		 * selected sequence name won't conflict; given sufficiently long		 * field names, two different serial columns in the same table could		 * be assigned the same sequence name, and we'd not notice since we		 * aren't creating the sequence quite yet.  In practice this seems		 * quite unlikely to be a problem, especially since few people would		 * need two serial columns in one table.		 */		snamespaceid = RangeVarGetCreationNamespace(cxt->relation);		snamespace = get_namespace_name(snamespaceid);		sname = ChooseRelationName(cxt->relation->relname,								   column->colname,								   "seq",								   snamespaceid);		ereport(NOTICE,				(errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",						cxt->stmtType, sname,						cxt->relation->relname, column->colname)));		/*		 * Build a CREATE SEQUENCE command to create the sequence object, and		 * add it to the list of things to be done before this CREATE/ALTER		 * TABLE.

⌨️ 快捷键说明

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