mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	New files for MERGE
This commit is contained in:
		
							
								
								
									
										660
									
								
								src/backend/parser/parse_merge.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										660
									
								
								src/backend/parser/parse_merge.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,660 @@ | ||||
| /*------------------------------------------------------------------------- | ||||
|  * | ||||
|  * parse_merge.c | ||||
|  *	  handle merge-statement in parser | ||||
|  * | ||||
|  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group | ||||
|  * Portions Copyright (c) 1994, Regents of the University of California | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  src/backend/parser/parse_merge.c | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
|  | ||||
| #include "postgres.h" | ||||
|  | ||||
| #include "miscadmin.h" | ||||
|  | ||||
| #include "access/sysattr.h" | ||||
| #include "nodes/makefuncs.h" | ||||
| #include "parser/analyze.h" | ||||
| #include "parser/parse_collate.h" | ||||
| #include "parser/parsetree.h" | ||||
| #include "parser/parser.h" | ||||
| #include "parser/parse_clause.h" | ||||
| #include "parser/parse_merge.h" | ||||
| #include "parser/parse_relation.h" | ||||
| #include "parser/parse_target.h" | ||||
| #include "utils/rel.h" | ||||
| #include "utils/relcache.h" | ||||
|  | ||||
| static int transformMergeJoinClause(ParseState *pstate, Node *merge, | ||||
| 						List **mergeSourceTargetList); | ||||
| static void setNamespaceForMergeAction(ParseState *pstate, | ||||
| 						MergeAction *action); | ||||
| static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, | ||||
| 							 bool rel_visible, | ||||
| 							 bool cols_visible); | ||||
| static List *expandSourceTL(ParseState *pstate, RangeTblEntry *rte, | ||||
| 							int rtindex); | ||||
|  | ||||
| /* | ||||
|  *	Special handling for MERGE statement is required because we assemble | ||||
|  *	the query manually. This is similar to setTargetTable() followed | ||||
|  * 	by transformFromClause() but with a few less steps. | ||||
|  * | ||||
|  *	Process the FROM clause and add items to the query's range table, | ||||
|  *	joinlist, and namespace. | ||||
|  * | ||||
|  *	A special targetlist comprising of the columns from the right-subtree of | ||||
|  *	the join is populated and returned. Note that when the JoinExpr is | ||||
|  *	setup by transformMergeStmt, the left subtree has the target result | ||||
|  *	relation and the right subtree has the source relation. | ||||
|  * | ||||
|  *	Returns the rangetable index of the target relation. | ||||
|  */ | ||||
| static int | ||||
| transformMergeJoinClause(ParseState *pstate, Node *merge, | ||||
| 						 List **mergeSourceTargetList) | ||||
| { | ||||
| 	RangeTblEntry *rte, | ||||
| 			   *rt_rte; | ||||
| 	List	   *namespace; | ||||
| 	int			rtindex, | ||||
| 				rt_rtindex; | ||||
| 	Node	   *n; | ||||
| 	int			mergeTarget_relation = list_length(pstate->p_rtable) + 1; | ||||
| 	Var		   *var; | ||||
| 	TargetEntry *te; | ||||
|  | ||||
| 	n = transformFromClauseItem(pstate, merge, | ||||
| 								&rte, | ||||
| 								&rtindex, | ||||
| 								&rt_rte, | ||||
| 								&rt_rtindex, | ||||
| 								&namespace); | ||||
|  | ||||
| 	pstate->p_joinlist = list_make1(n); | ||||
|  | ||||
| 	/* | ||||
| 	 * We created an internal join between the target and the source relation | ||||
| 	 * to carry out the MERGE actions. Normally such an unaliased join hides | ||||
| 	 * the joining relations, unless the column references are qualified. | ||||
| 	 * Also, any unqualified column references are resolved to the Join RTE, if | ||||
| 	 * there is a matching entry in the targetlist. But the way MERGE | ||||
| 	 * execution is later setup, we expect all column references to resolve to | ||||
| 	 * either the source or the target relation. Hence we must not add the | ||||
| 	 * Join RTE to the namespace. | ||||
| 	 * | ||||
| 	 * The last entry must be for the top-level Join RTE. We don't want to | ||||
| 	 * resolve any references to the Join RTE. So discard that. | ||||
| 	 * | ||||
| 	 * We also do not want to resolve any references from the leftside of the | ||||
| 	 * Join since that corresponds to the target relation. References to the | ||||
| 	 * columns of the target relation must be resolved from the result | ||||
| 	 * relation and not the one that is used in the join. So the | ||||
| 	 * mergeTarget_relation is marked invisible to both qualified as well as | ||||
| 	 * unqualified references. | ||||
| 	 */ | ||||
| 	Assert(list_length(namespace) > 1); | ||||
| 	namespace = list_truncate(namespace, list_length(namespace) - 1); | ||||
| 	pstate->p_namespace = list_concat(pstate->p_namespace, namespace); | ||||
|  | ||||
| 	setNamespaceVisibilityForRTE(pstate->p_namespace, | ||||
| 								 rt_fetch(mergeTarget_relation, pstate->p_rtable), false, false); | ||||
|  | ||||
| 	/* | ||||
| 	 * Expand the right relation and add its columns to the | ||||
| 	 * mergeSourceTargetList. Note that the right relation can either be a | ||||
| 	 * plain relation or a subquery or anything that can have a | ||||
| 	 * RangeTableEntry. | ||||
| 	 */ | ||||
| 	*mergeSourceTargetList = expandSourceTL(pstate, rt_rte, rt_rtindex); | ||||
|  | ||||
| 	/* | ||||
| 	 * Add a whole-row-Var entry to support references to "source.*". | ||||
| 	 */ | ||||
| 	var = makeWholeRowVar(rt_rte, rt_rtindex, 0, false); | ||||
| 	te = makeTargetEntry((Expr *) var, list_length(*mergeSourceTargetList) + 1, | ||||
| 						 NULL, true); | ||||
| 	*mergeSourceTargetList = lappend(*mergeSourceTargetList, te); | ||||
|  | ||||
| 	return mergeTarget_relation; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Make appropriate changes to the namespace visibility while transforming | ||||
|  * individual action's quals and targetlist expressions. In particular, for | ||||
|  * INSERT actions we must only see the source relation (since INSERT action is | ||||
|  * invoked for NOT MATCHED tuples and hence there is no target tuple to deal | ||||
|  * with). On the other hand, UPDATE and DELETE actions can see both source and | ||||
|  * target relations. | ||||
|  * | ||||
|  * Also, since the internal Join node can hide the source and target | ||||
|  * relations, we must explicitly make the respective relation as visible so | ||||
|  * that columns can be referenced unqualified from these relations. | ||||
|  */ | ||||
| static void | ||||
| setNamespaceForMergeAction(ParseState *pstate, MergeAction *action) | ||||
| { | ||||
| 	RangeTblEntry *targetRelRTE, | ||||
| 			   *sourceRelRTE; | ||||
|  | ||||
| 	/* Assume target relation is at index 1 */ | ||||
| 	targetRelRTE = rt_fetch(1, pstate->p_rtable); | ||||
|  | ||||
| 	/* | ||||
| 	 * Assume that the top-level join RTE is at the end. The source relation | ||||
| 	 * is just before that. | ||||
| 	 */ | ||||
| 	sourceRelRTE = rt_fetch(list_length(pstate->p_rtable) - 1, pstate->p_rtable); | ||||
|  | ||||
| 	switch (action->commandType) | ||||
| 	{ | ||||
| 		case CMD_INSERT: | ||||
|  | ||||
| 			/* | ||||
| 			 * Inserts can't see target relation, but they can see source | ||||
| 			 * relation. | ||||
| 			 */ | ||||
| 			setNamespaceVisibilityForRTE(pstate->p_namespace, | ||||
| 										 targetRelRTE, false, false); | ||||
| 			setNamespaceVisibilityForRTE(pstate->p_namespace, | ||||
| 										 sourceRelRTE, true, true); | ||||
| 			break; | ||||
|  | ||||
| 		case CMD_UPDATE: | ||||
| 		case CMD_DELETE: | ||||
|  | ||||
| 			/* | ||||
| 			 * Updates and deletes can see both target and source relations. | ||||
| 			 */ | ||||
| 			setNamespaceVisibilityForRTE(pstate->p_namespace, | ||||
| 										 targetRelRTE, true, true); | ||||
| 			setNamespaceVisibilityForRTE(pstate->p_namespace, | ||||
| 										 sourceRelRTE, true, true); | ||||
| 			break; | ||||
|  | ||||
| 		case CMD_NOTHING: | ||||
| 			break; | ||||
| 		default: | ||||
| 			elog(ERROR, "unknown action in MERGE WHEN clause"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * transformMergeStmt - | ||||
|  *	  transforms a MERGE statement | ||||
|  */ | ||||
| Query * | ||||
| transformMergeStmt(ParseState *pstate, MergeStmt *stmt) | ||||
| { | ||||
| 	Query		   *qry = makeNode(Query); | ||||
| 	ListCell	   *l; | ||||
| 	AclMode			targetPerms = ACL_NO_RIGHTS; | ||||
| 	bool			is_terminal[2]; | ||||
| 	JoinExpr	   *joinexpr; | ||||
| 	RangeTblEntry  *resultRelRTE, *mergeRelRTE; | ||||
|  | ||||
| 	/* There can't be any outer WITH to worry about */ | ||||
| 	Assert(pstate->p_ctenamespace == NIL); | ||||
|  | ||||
| 	qry->commandType = CMD_MERGE; | ||||
|  | ||||
| 	/* | ||||
| 	 * Check WHEN clauses for permissions and sanity | ||||
| 	 */ | ||||
| 	is_terminal[0] = false; | ||||
| 	is_terminal[1] = false; | ||||
| 	foreach(l, stmt->mergeActionList) | ||||
| 	{ | ||||
| 		MergeAction *action = (MergeAction *) lfirst(l); | ||||
| 		int		when_type = (action->matched ? 0 : 1); | ||||
|  | ||||
| 		/* | ||||
| 		 * Collect action types so we can check Target permissions | ||||
| 		 */ | ||||
| 		switch (action->commandType) | ||||
| 		{ | ||||
| 			case CMD_INSERT: | ||||
| 				{ | ||||
| 					InsertStmt *istmt = (InsertStmt *) action->stmt; | ||||
| 					SelectStmt *selectStmt = (SelectStmt *) istmt->selectStmt; | ||||
|  | ||||
| 					/* | ||||
| 					 * The grammar allows attaching ORDER BY, LIMIT, FOR | ||||
| 					 * UPDATE, or WITH to a VALUES clause and also multiple | ||||
| 					 * VALUES clauses. If we have any of those, ERROR. | ||||
| 					 */ | ||||
| 					if (selectStmt && (selectStmt->valuesLists == NIL || | ||||
| 									   selectStmt->sortClause != NIL || | ||||
| 									   selectStmt->limitOffset != NULL || | ||||
| 									   selectStmt->limitCount != NULL || | ||||
| 									   selectStmt->lockingClause != NIL || | ||||
| 									   selectStmt->withClause != NULL)) | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_SYNTAX_ERROR), | ||||
| 								 errmsg("SELECT not allowed in MERGE INSERT statement"))); | ||||
|  | ||||
| 					if (selectStmt && list_length(selectStmt->valuesLists) > 1) | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_SYNTAX_ERROR), | ||||
| 								 errmsg("Multiple VALUES clauses not allowed in MERGE INSERT statement"))); | ||||
|  | ||||
| 					targetPerms |= ACL_INSERT; | ||||
| 				} | ||||
| 				break; | ||||
| 			case CMD_UPDATE: | ||||
| 				targetPerms |= ACL_UPDATE; | ||||
| 				break; | ||||
| 			case CMD_DELETE: | ||||
| 				targetPerms |= ACL_DELETE; | ||||
| 				break; | ||||
| 			case CMD_NOTHING: | ||||
| 				break; | ||||
| 			default: | ||||
| 				elog(ERROR, "unknown action in MERGE WHEN clause"); | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * Check for unreachable WHEN clauses | ||||
| 		 */ | ||||
| 		if (action->condition == NULL) | ||||
| 			is_terminal[when_type] = true; | ||||
| 		else if (is_terminal[when_type]) | ||||
| 			ereport(ERROR, | ||||
| 					(errcode(ERRCODE_SYNTAX_ERROR), | ||||
| 					 errmsg("unreachable WHEN clause specified after unconditional WHEN clause"))); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Construct a query of the form | ||||
| 	 * 	SELECT relation.ctid	--junk attribute | ||||
| 	 *		  ,relation.tableoid	--junk attribute | ||||
| 	 * 		  ,source_relation.<somecols> | ||||
| 	 * 		  ,relation.<somecols> | ||||
| 	 *  FROM relation RIGHT JOIN source_relation | ||||
| 	 *  ON  join_condition; -- no WHERE clause - all conditions are applied in | ||||
| 	 * executor | ||||
| 	 * | ||||
| 	 * stmt->relation is the target relation, given as a RangeVar | ||||
| 	 * stmt->source_relation is a RangeVar or subquery | ||||
| 	 * | ||||
| 	 * We specify the join as a RIGHT JOIN as a simple way of forcing the | ||||
| 	 * first (larg) RTE to refer to the target table. | ||||
| 	 * | ||||
| 	 * The MERGE query's join can be tuned in some cases, see below for these | ||||
| 	 * special case tweaks. | ||||
| 	 * | ||||
| 	 * We set QSRC_PARSER to show query constructed in parse analysis | ||||
| 	 * | ||||
| 	 * Note that we have only one Query for a MERGE statement and the planner | ||||
| 	 * is called only once. That query is executed once to produce our stream | ||||
| 	 * of candidate change rows, so the query must contain all of the columns | ||||
| 	 * required by each of the targetlist or conditions for each action. | ||||
| 	 * | ||||
| 	 * As top-level statements INSERT, UPDATE and DELETE have a Query, whereas | ||||
| 	 * with MERGE the individual actions do not require separate planning, | ||||
| 	 * only different handling in the executor. See nodeModifyTable handling | ||||
| 	 * of commandType CMD_MERGE. | ||||
| 	 * | ||||
| 	 * A sub-query can include the Target, but otherwise the sub-query cannot | ||||
| 	 * reference the outermost Target table at all. | ||||
| 	 */ | ||||
| 	qry->querySource = QSRC_PARSER; | ||||
|  | ||||
| 	/* | ||||
| 	 * Setup the target table. Unlike regular UPDATE/DELETE, we don't expand | ||||
| 	 * inheritance for the target relation in case of MERGE. | ||||
| 	 * | ||||
| 	 * This special arrangement is required for handling partitioned tables | ||||
| 	 * because we perform an JOIN between the target and the source relation to | ||||
| 	 * identify the matching and not-matching rows. If we take the usual path | ||||
| 	 * of expanding the target table's inheritance and create one subplan per | ||||
| 	 * partition, then we we won't be able to correctly identify the matching | ||||
| 	 * and not-matching rows since for a given source row, there may not be a | ||||
| 	 * matching row in one partition, but it may exists in some other | ||||
| 	 * partition. So we must first append all the qualifying rows from all the | ||||
| 	 * partitions and then do the matching. | ||||
| 	 * | ||||
| 	 * Once a target row is returned by the underlying join, we find the | ||||
| 	 * correct partition and setup required state to carry out UPDATE/DELETE. | ||||
| 	 * All of this happens during execution. | ||||
| 	 */ | ||||
| 	qry->resultRelation = setTargetTable(pstate, stmt->relation, | ||||
| 										 false,	/* do not expand inheritance */ | ||||
| 										 true, targetPerms); | ||||
|  | ||||
| 	/* | ||||
| 	 * Create a JOIN between the target and the source relation. | ||||
| 	 */ | ||||
| 	joinexpr = makeNode(JoinExpr); | ||||
| 	joinexpr->isNatural = false; | ||||
| 	joinexpr->alias = NULL; | ||||
| 	joinexpr->usingClause = NIL; | ||||
| 	joinexpr->quals = stmt->join_condition; | ||||
| 	joinexpr->larg = (Node *) stmt->relation; | ||||
| 	joinexpr->rarg = (Node *) stmt->source_relation; | ||||
|  | ||||
| 	/* | ||||
| 	 * Simplify the MERGE query as much as possible | ||||
| 	 * | ||||
| 	 * These seem like things that could go into Optimizer, but they are | ||||
| 	 * semantic simplifications rather than optimizations, per se. | ||||
| 	 * | ||||
| 	 * If there are no INSERT actions we won't be using the non-matching | ||||
| 	 * candidate rows for anything, so no need for an outer join. We do still | ||||
| 	 * need an inner join for UPDATE and DELETE actions. | ||||
| 	 */ | ||||
| 	if (targetPerms & ACL_INSERT) | ||||
| 		joinexpr->jointype = JOIN_RIGHT; | ||||
| 	else | ||||
| 		joinexpr->jointype = JOIN_INNER; | ||||
|  | ||||
| 	/* | ||||
| 	 * We use a special purpose transformation here because the normal | ||||
| 	 * routines don't quite work right for the MERGE case. | ||||
| 	 * | ||||
| 	 * A special mergeSourceTargetList is setup by transformMergeJoinClause(). | ||||
| 	 * It refers to all the attributes provided by the source relation. This | ||||
| 	 * is later used by set_plan_refs() to fix the UPDATE/INSERT target lists | ||||
| 	 * to so that they can correctly fetch the attributes from the source | ||||
| 	 * relation. | ||||
| 	 * | ||||
| 	 * The target relation when used in the underlying join, gets a new RTE | ||||
| 	 * with rte->inh set to true. We remember this RTE (and later pass on to | ||||
| 	 * the planner and executor) for two main reasons: | ||||
| 	 * | ||||
| 	 * 1. If we ever need to run EvalPlanQual while performing MERGE, we must | ||||
| 	 * make the modified tuple available to the underlying join query, which is | ||||
| 	 * using a different RTE from the resultRelation RTE. | ||||
| 	 * | ||||
| 	 * 2. rewriteTargetListMerge() requires the RTE of the underlying join in | ||||
| 	 * order to add junk CTID and TABLEOID attributes. | ||||
| 	 */ | ||||
| 	qry->mergeTarget_relation = transformMergeJoinClause(pstate, (Node *) joinexpr, | ||||
| 														 &qry->mergeSourceTargetList); | ||||
|  | ||||
| 	/* | ||||
| 	 * The target table referenced in the MERGE is looked up twice; once while | ||||
| 	 * setting it up as the result relation and again when it's used in the | ||||
| 	 * underlying the join query. In some rare situations, it may happen that | ||||
| 	 * these lookups return different results, for example, if a new relation | ||||
| 	 * with the same name gets created in a schema which is ahead in the | ||||
| 	 * search_path, in between the two lookups. | ||||
| 	 * | ||||
| 	 * It's a very narrow case, but nevertheless we guard against it by simply | ||||
| 	 * checking if the OIDs returned by the two lookups is the same. If not, we | ||||
| 	 * just throw an error. | ||||
| 	 */ | ||||
| 	Assert(qry->resultRelation > 0); | ||||
| 	Assert(qry->mergeTarget_relation > 0); | ||||
|  | ||||
| 	/* Fetch both the RTEs */ | ||||
| 	resultRelRTE = rt_fetch(qry->resultRelation, pstate->p_rtable); | ||||
| 	mergeRelRTE = rt_fetch(qry->mergeTarget_relation, pstate->p_rtable); | ||||
|  | ||||
| 	if (resultRelRTE->relid != mergeRelRTE->relid) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), | ||||
| 				 errmsg("relation referenced by MERGE statement has changed"))); | ||||
|  | ||||
| 	/* | ||||
| 	 * This query should just provide the source relation columns. Later, in | ||||
| 	 * preprocess_targetlist(), we shall also add "ctid" attribute of the | ||||
| 	 * target relation to ensure that the target tuple can be fetched | ||||
| 	 * correctly. | ||||
| 	 */ | ||||
| 	qry->targetList = qry->mergeSourceTargetList; | ||||
|  | ||||
| 	/* qry has no WHERE clause so absent quals are shown as NULL */ | ||||
| 	qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); | ||||
| 	qry->rtable = pstate->p_rtable; | ||||
|  | ||||
| 	/* | ||||
| 	 * XXX MERGE is unsupported in various cases | ||||
| 	 */ | ||||
| 	if (!(pstate->p_target_relation->rd_rel->relkind == RELKIND_RELATION || | ||||
| 		  pstate->p_target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 				 errmsg("MERGE is not supported for this relation type"))); | ||||
|  | ||||
| 	if (pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE && | ||||
| 		pstate->p_target_relation->rd_rel->relhassubclass) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 				 errmsg("MERGE is not supported for relations with inheritance"))); | ||||
|  | ||||
| 	if (pstate->p_target_relation->rd_rel->relhasrules) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 				 errmsg("MERGE is not supported for relations with rules"))); | ||||
|  | ||||
| 	/* | ||||
| 	 * We now have a good query shape, so now look at the when conditions and | ||||
| 	 * action targetlists. | ||||
| 	 * | ||||
| 	 * Overall, the MERGE Query's targetlist is NIL. | ||||
| 	 * | ||||
| 	 * Each individual action has its own targetlist that needs separate | ||||
| 	 * transformation. These transforms don't do anything to the overall | ||||
| 	 * targetlist, since that is only used for resjunk columns. | ||||
| 	 * | ||||
| 	 * We can reference any column in Target or Source, which is OK because | ||||
| 	 * both of those already have RTEs. There is nothing like the EXCLUDED | ||||
| 	 * pseudo-relation for INSERT ON CONFLICT. | ||||
| 	 */ | ||||
| 	foreach(l, stmt->mergeActionList) | ||||
| 	{ | ||||
| 		MergeAction *action = (MergeAction *) lfirst(l); | ||||
|  | ||||
| 		/* | ||||
| 		 * Set namespace for the specific action. This must be done before | ||||
| 		 * analyzing the WHEN quals and the action targetlisst. | ||||
| 		 */ | ||||
| 		setNamespaceForMergeAction(pstate, action); | ||||
|  | ||||
| 		/* | ||||
| 		 * Transform the when condition. | ||||
| 		 * | ||||
| 		 * Note that these quals are NOT added to the join quals; instead they | ||||
| 		 * are evaluated separately during execution to decide which of the | ||||
| 		 * WHEN MATCHED or WHEN NOT MATCHED actions to execute. | ||||
| 		 */ | ||||
| 		action->qual = transformWhereClause(pstate, action->condition, | ||||
| 											EXPR_KIND_MERGE_WHEN_AND, "WHEN"); | ||||
|  | ||||
| 		/* | ||||
| 		 * Transform target lists for each INSERT and UPDATE action stmt | ||||
| 		 */ | ||||
| 		switch (action->commandType) | ||||
| 		{ | ||||
| 			case CMD_INSERT: | ||||
| 				{ | ||||
| 					InsertStmt *istmt = (InsertStmt *) action->stmt; | ||||
| 					SelectStmt *selectStmt = (SelectStmt *) istmt->selectStmt; | ||||
| 					List	   *exprList = NIL; | ||||
| 					ListCell   *lc; | ||||
| 					RangeTblEntry *rte; | ||||
| 					ListCell   *icols; | ||||
| 					ListCell   *attnos; | ||||
| 					List	   *icolumns; | ||||
| 					List	   *attrnos; | ||||
|  | ||||
| 					pstate->p_is_insert = true; | ||||
|  | ||||
| 					icolumns = checkInsertTargets(pstate, istmt->cols, &attrnos); | ||||
| 					Assert(list_length(icolumns) == list_length(attrnos)); | ||||
|  | ||||
| 					/* | ||||
| 					 * Handle INSERT much like in transformInsertStmt | ||||
| 					 */ | ||||
| 					if (selectStmt == NULL) | ||||
| 					{ | ||||
| 						/* | ||||
| 						 * We have INSERT ... DEFAULT VALUES.  We can handle | ||||
| 						 * this case by emitting an empty targetlist --- all | ||||
| 						 * columns will be defaulted when the planner expands | ||||
| 						 * the targetlist. | ||||
| 						 */ | ||||
| 						exprList = NIL; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/* | ||||
| 						 * Process INSERT ... VALUES with a single VALUES | ||||
| 						 * sublist.  We treat this case separately for | ||||
| 						 * efficiency.  The sublist is just computed directly | ||||
| 						 * as the Query's targetlist, with no VALUES RTE.  So | ||||
| 						 * it works just like a SELECT without any FROM. | ||||
| 						 */ | ||||
| 						List	   *valuesLists = selectStmt->valuesLists; | ||||
|  | ||||
| 						Assert(list_length(valuesLists) == 1); | ||||
| 						Assert(selectStmt->intoClause == NULL); | ||||
|  | ||||
| 						/* | ||||
| 						 * Do basic expression transformation (same as a ROW() | ||||
| 						 * expr, but allow SetToDefault at top level) | ||||
| 						 */ | ||||
| 						exprList = transformExpressionList(pstate, | ||||
| 														   (List *) linitial(valuesLists), | ||||
| 														   EXPR_KIND_VALUES_SINGLE, | ||||
| 														   true); | ||||
|  | ||||
| 						/* Prepare row for assignment to target table */ | ||||
| 						exprList = transformInsertRow(pstate, exprList, | ||||
| 													  istmt->cols, | ||||
| 													  icolumns, attrnos, | ||||
| 													  false); | ||||
| 					} | ||||
|  | ||||
| 					/* | ||||
| 					 * Generate action's target list using the computed list | ||||
| 					 * of expressions. Also, mark all the target columns as | ||||
| 					 * needing insert permissions. | ||||
| 					 */ | ||||
| 					rte = pstate->p_target_rangetblentry; | ||||
| 					icols = list_head(icolumns); | ||||
| 					attnos = list_head(attrnos); | ||||
| 					foreach(lc, exprList) | ||||
| 					{ | ||||
| 						Expr	   *expr = (Expr *) lfirst(lc); | ||||
| 						ResTarget  *col; | ||||
| 						AttrNumber	attr_num; | ||||
| 						TargetEntry *tle; | ||||
|  | ||||
| 						col = lfirst_node(ResTarget, icols); | ||||
| 						attr_num = (AttrNumber) lfirst_int(attnos); | ||||
|  | ||||
| 						tle = makeTargetEntry(expr, | ||||
| 											  attr_num, | ||||
| 											  col->name, | ||||
| 											  false); | ||||
| 						action->targetList = lappend(action->targetList, tle); | ||||
|  | ||||
| 						rte->insertedCols = bms_add_member(rte->insertedCols, | ||||
| 														   attr_num - FirstLowInvalidHeapAttributeNumber); | ||||
|  | ||||
| 						icols = lnext(icols); | ||||
| 						attnos = lnext(attnos); | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 			case CMD_UPDATE: | ||||
| 				{ | ||||
| 					UpdateStmt *ustmt = (UpdateStmt *) action->stmt; | ||||
|  | ||||
| 					pstate->p_is_insert = false; | ||||
| 					action->targetList = transformUpdateTargetList(pstate, ustmt->targetList); | ||||
| 				} | ||||
| 				break; | ||||
| 			case CMD_DELETE: | ||||
| 				break; | ||||
|  | ||||
| 			case CMD_NOTHING: | ||||
| 				action->targetList = NIL; | ||||
| 				break; | ||||
| 			default: | ||||
| 				elog(ERROR, "unknown action in MERGE WHEN clause"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	qry->mergeActionList = stmt->mergeActionList; | ||||
|  | ||||
| 	/* XXX maybe later */ | ||||
| 	qry->returningList = NULL; | ||||
|  | ||||
| 	qry->hasTargetSRFs = false; | ||||
| 	qry->hasSubLinks = pstate->p_hasSubLinks; | ||||
|  | ||||
| 	assign_query_collations(pstate, qry); | ||||
|  | ||||
| 	return qry; | ||||
| } | ||||
|  | ||||
| static void | ||||
| setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, | ||||
| 							 bool rel_visible, | ||||
| 							 bool cols_visible) | ||||
| { | ||||
| 	ListCell   *lc; | ||||
|  | ||||
| 	foreach(lc, namespace) | ||||
| 	{ | ||||
| 		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc); | ||||
|  | ||||
| 		if (nsitem->p_rte == rte) | ||||
| 		{ | ||||
| 			nsitem->p_rel_visible = rel_visible; | ||||
| 			nsitem->p_cols_visible = cols_visible; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Expand the source relation to include all attributes of this RTE. | ||||
|  * | ||||
|  * This function is very similar to expandRelAttrs except that we don't mark | ||||
|  * columns for SELECT privileges. That will be decided later when we transform | ||||
|  * the action targetlists and the WHEN quals for actual references to the | ||||
|  * source relation. | ||||
|  */ | ||||
| static List * | ||||
| expandSourceTL(ParseState *pstate, RangeTblEntry *rte, int rtindex) | ||||
| { | ||||
| 	List	   *names, | ||||
| 			   *vars; | ||||
| 	ListCell   *name, | ||||
| 			   *var; | ||||
| 	List	   *te_list = NIL; | ||||
|  | ||||
| 	expandRTE(rte, rtindex, 0, -1, false, &names, &vars); | ||||
|  | ||||
| 	/* | ||||
| 	 * Require read access to the table. | ||||
| 	 */ | ||||
| 	rte->requiredPerms |= ACL_SELECT; | ||||
|  | ||||
| 	forboth(name, names, var, vars) | ||||
| 	{ | ||||
| 		char	   *label = strVal(lfirst(name)); | ||||
| 		Var		   *varnode = (Var *) lfirst(var); | ||||
| 		TargetEntry *te; | ||||
|  | ||||
| 		te = makeTargetEntry((Expr *) varnode, | ||||
| 							 (AttrNumber) pstate->p_next_resno++, | ||||
| 							 label, | ||||
| 							 false); | ||||
| 		te_list = lappend(te_list, te); | ||||
| 	} | ||||
|  | ||||
| 	Assert(name == NULL && var == NULL);	/* lists not the same length? */ | ||||
|  | ||||
| 	return te_list; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user