mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	Revert MERGE patch
This reverts commitsd204ef6377,83454e3c2band a few more commits thereafter (complete list at the end) related to MERGE feature. While the feature was fully functional, with sufficient test coverage and necessary documentation, it was felt that some parts of the executor and parse-analyzer can use a different design and it wasn't possible to do that in the available time. So it was decided to revert the patch for PG11 and retry again in the future. Thanks again to all reviewers and bug reporters. List of commits reverted, in reverse chronological order:f1464c5380Improve parse representation for MERGEddb4158579MERGE syntax diagram correction530e69e59bAllow cpluspluscheck to pass by renaming variable01b88b4df5MERGE minor errata3af7b2b0d4MERGE fix variable warning in non-assert buildsa5d86181ecMERGE INSERT allows only one VALUES clause4b2d44031fMERGE post-commit review4923550c20Tab completion for MERGEaa3faa3c7aWITH support in MERGE83454e3c2bNew files for MERGEd204ef6377MERGE SQL Command following SQL:2016 Author: Pavan Deolasee Reviewed-by: Michael Paquier
This commit is contained in:
		| @@ -14,7 +14,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) | ||||
|  | ||||
| OBJS= analyze.o gram.o scan.o parser.o \ | ||||
|       parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \ | ||||
|       parse_enr.o parse_expr.o parse_func.o parse_merge.o parse_node.o parse_oper.o \ | ||||
|       parse_enr.o parse_expr.o parse_func.o parse_node.o parse_oper.o \ | ||||
|       parse_param.o parse_relation.o parse_target.o parse_type.o \ | ||||
|       parse_utilcmd.o scansup.o | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,6 @@ | ||||
| #include "parser/parse_cte.h" | ||||
| #include "parser/parse_expr.h" | ||||
| #include "parser/parse_func.h" | ||||
| #include "parser/parse_merge.h" | ||||
| #include "parser/parse_oper.h" | ||||
| #include "parser/parse_param.h" | ||||
| #include "parser/parse_relation.h" | ||||
| @@ -54,6 +53,9 @@ post_parse_analyze_hook_type post_parse_analyze_hook = NULL; | ||||
| static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree); | ||||
| static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); | ||||
| static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); | ||||
| static List *transformInsertRow(ParseState *pstate, List *exprlist, | ||||
| 				   List *stmtcols, List *icolumns, List *attrnos, | ||||
| 				   bool strip_indirection); | ||||
| static OnConflictExpr *transformOnConflictClause(ParseState *pstate, | ||||
| 						  OnConflictClause *onConflictClause); | ||||
| static int	count_rowexpr_columns(ParseState *pstate, Node *expr); | ||||
| @@ -66,6 +68,8 @@ static void determineRecursiveColTypes(ParseState *pstate, | ||||
| 						   Node *larg, List *nrtargetlist); | ||||
| static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); | ||||
| static List *transformReturningList(ParseState *pstate, List *returningList); | ||||
| static List *transformUpdateTargetList(ParseState *pstate, | ||||
| 						  List *targetList); | ||||
| static Query *transformDeclareCursorStmt(ParseState *pstate, | ||||
| 						   DeclareCursorStmt *stmt); | ||||
| static Query *transformExplainStmt(ParseState *pstate, | ||||
| @@ -263,7 +267,6 @@ transformStmt(ParseState *pstate, Node *parseTree) | ||||
| 		case T_InsertStmt: | ||||
| 		case T_UpdateStmt: | ||||
| 		case T_DeleteStmt: | ||||
| 		case T_MergeStmt: | ||||
| 			(void) test_raw_expression_coverage(parseTree, NULL); | ||||
| 			break; | ||||
| 		default: | ||||
| @@ -288,10 +291,6 @@ transformStmt(ParseState *pstate, Node *parseTree) | ||||
| 			result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); | ||||
| 			break; | ||||
|  | ||||
| 		case T_MergeStmt: | ||||
| 			result = transformMergeStmt(pstate, (MergeStmt *) parseTree); | ||||
| 			break; | ||||
|  | ||||
| 		case T_SelectStmt: | ||||
| 			{ | ||||
| 				SelectStmt *n = (SelectStmt *) parseTree; | ||||
| @@ -367,7 +366,6 @@ analyze_requires_snapshot(RawStmt *parseTree) | ||||
| 		case T_InsertStmt: | ||||
| 		case T_DeleteStmt: | ||||
| 		case T_UpdateStmt: | ||||
| 		case T_MergeStmt: | ||||
| 		case T_SelectStmt: | ||||
| 			result = true; | ||||
| 			break; | ||||
| @@ -898,7 +896,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) | ||||
|  * attrnos: integer column numbers (must be same length as icolumns) | ||||
|  * strip_indirection: if true, remove any field/array assignment nodes | ||||
|  */ | ||||
| List * | ||||
| static List * | ||||
| transformInsertRow(ParseState *pstate, List *exprlist, | ||||
| 				   List *stmtcols, List *icolumns, List *attrnos, | ||||
| 				   bool strip_indirection) | ||||
| @@ -2262,9 +2260,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) | ||||
|  | ||||
| /* | ||||
|  * transformUpdateTargetList - | ||||
|  *	handle SET clause in UPDATE/MERGE/INSERT ... ON CONFLICT UPDATE | ||||
|  *	handle SET clause in UPDATE/INSERT ... ON CONFLICT UPDATE | ||||
|  */ | ||||
| List * | ||||
| static List * | ||||
| transformUpdateTargetList(ParseState *pstate, List *origTlist) | ||||
| { | ||||
| 	List	   *tlist = NIL; | ||||
|   | ||||
| @@ -241,7 +241,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| 	PartitionSpec		*partspec; | ||||
| 	PartitionBoundSpec	*partboundspec; | ||||
| 	RoleSpec			*rolespec; | ||||
| 	MergeWhenClause		*mergewhen; | ||||
| } | ||||
|  | ||||
| %type <node>	stmt schema_stmt | ||||
| @@ -283,7 +282,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt | ||||
| 		CreatePublicationStmt AlterPublicationStmt | ||||
| 		CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt | ||||
| 		MergeStmt | ||||
|  | ||||
| %type <node>	select_no_parens select_with_parens select_clause | ||||
| 				simple_select values_clause | ||||
| @@ -402,7 +400,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| 				TriggerTransitions TriggerReferencing | ||||
| 				publication_name_list | ||||
| 				vacuum_relation_list opt_vacuum_relation_list | ||||
| 				merge_values_clause | ||||
|  | ||||
| %type <list>	group_by_list | ||||
| %type <node>	group_by_item empty_grouping_set rollup_clause cube_clause | ||||
| @@ -463,7 +460,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| %type <istmt>	insert_rest | ||||
| %type <infer>	opt_conf_expr | ||||
| %type <onconflict> opt_on_conflict | ||||
| %type <mergewhen>	merge_insert merge_update merge_delete | ||||
|  | ||||
| %type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest | ||||
| 				 SetResetClause FunctionSetResetClause | ||||
| @@ -589,9 +585,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| %type <list>		hash_partbound partbound_datum_list range_datum_list | ||||
| %type <defelt>		hash_partbound_elem | ||||
|  | ||||
| %type <node>	merge_when_clause opt_merge_when_and_condition | ||||
| %type <list>	merge_when_list | ||||
|  | ||||
| /* | ||||
|  * Non-keyword token types.  These are hard-wired into the "flex" lexer. | ||||
|  * They must be listed first so that their numeric codes do not depend on | ||||
| @@ -659,8 +652,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL | ||||
| 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED | ||||
|  | ||||
| 	MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD | ||||
| 	MINUTE_P MINVALUE MODE MONTH_P MOVE | ||||
| 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE | ||||
|  | ||||
| 	NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NONE | ||||
| 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF | ||||
| @@ -929,7 +921,6 @@ stmt : | ||||
| 			| RefreshMatViewStmt | ||||
| 			| LoadStmt | ||||
| 			| LockStmt | ||||
| 			| MergeStmt | ||||
| 			| NotifyStmt | ||||
| 			| PrepareStmt | ||||
| 			| ReassignOwnedStmt | ||||
| @@ -10689,7 +10680,6 @@ ExplainableStmt: | ||||
| 			| InsertStmt | ||||
| 			| UpdateStmt | ||||
| 			| DeleteStmt | ||||
| 			| MergeStmt | ||||
| 			| DeclareCursorStmt | ||||
| 			| CreateAsStmt | ||||
| 			| CreateMatViewStmt | ||||
| @@ -10752,7 +10742,6 @@ PreparableStmt: | ||||
| 			| InsertStmt | ||||
| 			| UpdateStmt | ||||
| 			| DeleteStmt					/* by default all are $$=$1 */ | ||||
| 			| MergeStmt | ||||
| 		; | ||||
|  | ||||
| /***************************************************************************** | ||||
| @@ -11119,142 +11108,6 @@ set_target_list: | ||||
| 		; | ||||
|  | ||||
|  | ||||
| /***************************************************************************** | ||||
|  * | ||||
|  *		QUERY: | ||||
|  *				MERGE STATEMENTS | ||||
|  * | ||||
|  *****************************************************************************/ | ||||
|  | ||||
| MergeStmt: | ||||
| 			opt_with_clause MERGE INTO relation_expr_opt_alias | ||||
| 			USING table_ref | ||||
| 			ON a_expr | ||||
| 			merge_when_list | ||||
| 				{ | ||||
| 					MergeStmt *m = makeNode(MergeStmt); | ||||
|  | ||||
| 					m->withClause = $1; | ||||
| 					m->relation = $4; | ||||
| 					m->source_relation = $6; | ||||
| 					m->join_condition = $8; | ||||
| 					m->mergeWhenClauses = $9; | ||||
|  | ||||
| 					$$ = (Node *)m; | ||||
| 				} | ||||
| 			; | ||||
|  | ||||
|  | ||||
| merge_when_list: | ||||
| 			merge_when_clause						{ $$ = list_make1($1); } | ||||
| 			| merge_when_list merge_when_clause		{ $$ = lappend($1,$2); } | ||||
| 			; | ||||
|  | ||||
| merge_when_clause: | ||||
| 			WHEN MATCHED opt_merge_when_and_condition THEN merge_update | ||||
| 				{ | ||||
| 					$5->matched = true; | ||||
| 					$5->commandType = CMD_UPDATE; | ||||
| 					$5->condition = $3; | ||||
|  | ||||
| 					$$ = (Node *) $5; | ||||
| 				} | ||||
| 			| WHEN MATCHED opt_merge_when_and_condition THEN merge_delete | ||||
| 				{ | ||||
| 					MergeWhenClause *m = makeNode(MergeWhenClause); | ||||
|  | ||||
| 					m->matched = true; | ||||
| 					m->commandType = CMD_DELETE; | ||||
| 					m->condition = $3; | ||||
|  | ||||
| 					$$ = (Node *)m; | ||||
| 				} | ||||
| 			| WHEN NOT MATCHED opt_merge_when_and_condition THEN merge_insert | ||||
| 				{ | ||||
| 					$6->matched = false; | ||||
| 					$6->commandType = CMD_INSERT; | ||||
| 					$6->condition = $4; | ||||
|  | ||||
| 					$$ = (Node *) $6; | ||||
| 				} | ||||
| 			| WHEN NOT MATCHED opt_merge_when_and_condition THEN DO NOTHING | ||||
| 				{ | ||||
| 					MergeWhenClause *m = makeNode(MergeWhenClause); | ||||
|  | ||||
| 					m->matched = false; | ||||
| 					m->commandType = CMD_NOTHING; | ||||
| 					m->condition = $4; | ||||
|  | ||||
| 					$$ = (Node *)m; | ||||
| 				} | ||||
| 			; | ||||
|  | ||||
| opt_merge_when_and_condition: | ||||
| 			AND a_expr 				{ $$ = $2; } | ||||
| 			| 			 			{ $$ = NULL; } | ||||
| 			; | ||||
|  | ||||
| merge_delete: | ||||
| 			DELETE_P 				{ $$ = NULL; } | ||||
| 			; | ||||
|  | ||||
| merge_update: | ||||
| 			UPDATE SET set_clause_list | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->targetList = $3; | ||||
|  | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			; | ||||
|  | ||||
| merge_insert: | ||||
| 			INSERT merge_values_clause | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->cols = NIL; | ||||
| 					n->values = $2; | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			| INSERT OVERRIDING override_kind VALUE_P merge_values_clause | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->cols = NIL; | ||||
| 					n->override = $3; | ||||
| 					n->values = $5; | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			| INSERT '(' insert_column_list ')' merge_values_clause | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->cols = $3; | ||||
| 					n->values = $5; | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			| INSERT '(' insert_column_list ')' OVERRIDING override_kind VALUE_P merge_values_clause | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->cols = $3; | ||||
| 					n->override = $6; | ||||
| 					n->values = $8; | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			| INSERT DEFAULT VALUES | ||||
| 				{ | ||||
| 					MergeWhenClause *n = makeNode(MergeWhenClause); | ||||
| 					n->cols = NIL; | ||||
| 					n->values = NIL; | ||||
| 					$$ = n; | ||||
| 				} | ||||
| 			; | ||||
|  | ||||
| merge_values_clause: | ||||
| 			VALUES '(' expr_list ')' | ||||
| 				{ | ||||
| 					$$ = $3; | ||||
| 				} | ||||
| 			; | ||||
|  | ||||
| /***************************************************************************** | ||||
|  * | ||||
|  *		QUERY: | ||||
| @@ -15256,10 +15109,8 @@ unreserved_keyword: | ||||
| 			| LOGGED | ||||
| 			| MAPPING | ||||
| 			| MATCH | ||||
| 			| MATCHED | ||||
| 			| MATERIALIZED | ||||
| 			| MAXVALUE | ||||
| 			| MERGE | ||||
| 			| METHOD | ||||
| 			| MINUTE_P | ||||
| 			| MINVALUE | ||||
|   | ||||
| @@ -455,13 +455,6 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) | ||||
| 		case EXPR_KIND_VALUES_SINGLE: | ||||
| 			errkind = true; | ||||
| 			break; | ||||
| 		case EXPR_KIND_MERGE_WHEN_AND: | ||||
| 			if (isAgg) | ||||
| 				err = _("aggregate functions are not allowed in WHEN AND conditions"); | ||||
| 			else | ||||
| 				err = _("grouping operations are not allowed in WHEN AND conditions"); | ||||
|  | ||||
| 			break; | ||||
| 		case EXPR_KIND_CHECK_CONSTRAINT: | ||||
| 		case EXPR_KIND_DOMAIN_CHECK: | ||||
| 			if (isAgg) | ||||
| @@ -880,9 +873,6 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, | ||||
| 		case EXPR_KIND_VALUES_SINGLE: | ||||
| 			errkind = true; | ||||
| 			break; | ||||
| 		case EXPR_KIND_MERGE_WHEN_AND: | ||||
| 			err = _("window functions are not allowed in WHEN AND conditions"); | ||||
| 			break; | ||||
| 		case EXPR_KIND_CHECK_CONSTRAINT: | ||||
| 		case EXPR_KIND_DOMAIN_CHECK: | ||||
| 			err = _("window functions are not allowed in check constraints"); | ||||
|   | ||||
| @@ -76,6 +76,9 @@ static RangeTblEntry *transformRangeTableFunc(ParseState *pstate, | ||||
| 						RangeTableFunc *t); | ||||
| static TableSampleClause *transformRangeTableSample(ParseState *pstate, | ||||
| 						  RangeTableSample *rts); | ||||
| static Node *transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 						RangeTblEntry **top_rte, int *top_rti, | ||||
| 						List **namespace); | ||||
| static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, | ||||
| 				   Var *l_colvar, Var *r_colvar); | ||||
| static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, | ||||
| @@ -136,7 +139,6 @@ transformFromClause(ParseState *pstate, List *frmList) | ||||
| 		n = transformFromClauseItem(pstate, n, | ||||
| 									&rte, | ||||
| 									&rtindex, | ||||
| 									NULL, NULL, | ||||
| 									&namespace); | ||||
|  | ||||
| 		checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace); | ||||
| @@ -1094,21 +1096,14 @@ getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv) | ||||
|  * | ||||
|  * *top_rti: receives the rangetable index of top_rte.  (Ditto.) | ||||
|  * | ||||
|  * *right_rte: receives the RTE corresponding to the right side of the | ||||
|  * jointree. Only MERGE really needs to know about this and only MERGE passes a | ||||
|  * non-NULL pointer. | ||||
|  * | ||||
|  * *right_rti: receives the rangetable index of the right_rte. | ||||
|  * | ||||
|  * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed | ||||
|  * as table/column names by this item.  (The lateral_only flags in these items | ||||
|  * are indeterminate and should be explicitly set by the caller before use.) | ||||
|  */ | ||||
| Node * | ||||
| static Node * | ||||
| transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 						RangeTblEntry **top_rte, int *top_rti, | ||||
| 						RangeTblEntry **right_rte, int *right_rti, | ||||
| 						List **fnamespace) | ||||
| 						List **namespace) | ||||
| { | ||||
| 	if (IsA(n, RangeVar)) | ||||
| 	{ | ||||
| @@ -1130,7 +1125,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); | ||||
| 		*top_rte = rte; | ||||
| 		*top_rti = rtindex; | ||||
| 		*fnamespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		*namespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		rtr = makeNode(RangeTblRef); | ||||
| 		rtr->rtindex = rtindex; | ||||
| 		return (Node *) rtr; | ||||
| @@ -1148,7 +1143,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); | ||||
| 		*top_rte = rte; | ||||
| 		*top_rti = rtindex; | ||||
| 		*fnamespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		*namespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		rtr = makeNode(RangeTblRef); | ||||
| 		rtr->rtindex = rtindex; | ||||
| 		return (Node *) rtr; | ||||
| @@ -1166,7 +1161,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); | ||||
| 		*top_rte = rte; | ||||
| 		*top_rti = rtindex; | ||||
| 		*fnamespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		*namespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		rtr = makeNode(RangeTblRef); | ||||
| 		rtr->rtindex = rtindex; | ||||
| 		return (Node *) rtr; | ||||
| @@ -1184,7 +1179,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); | ||||
| 		*top_rte = rte; | ||||
| 		*top_rti = rtindex; | ||||
| 		*fnamespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		*namespace = list_make1(makeDefaultNSItem(rte)); | ||||
| 		rtr = makeNode(RangeTblRef); | ||||
| 		rtr->rtindex = rtindex; | ||||
| 		return (Node *) rtr; | ||||
| @@ -1199,7 +1194,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
|  | ||||
| 		/* Recursively transform the contained relation */ | ||||
| 		rel = transformFromClauseItem(pstate, rts->relation, | ||||
| 									  top_rte, top_rti, NULL, NULL, fnamespace); | ||||
| 									  top_rte, top_rti, namespace); | ||||
| 		/* Currently, grammar could only return a RangeVar as contained rel */ | ||||
| 		rtr = castNode(RangeTblRef, rel); | ||||
| 		rte = rt_fetch(rtr->rtindex, pstate->p_rtable); | ||||
| @@ -1227,7 +1222,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		List	   *l_namespace, | ||||
| 				   *r_namespace, | ||||
| 				   *my_namespace, | ||||
| 				   *save_namespace, | ||||
| 				   *l_colnames, | ||||
| 				   *r_colnames, | ||||
| 				   *res_colnames, | ||||
| @@ -1246,7 +1240,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		j->larg = transformFromClauseItem(pstate, j->larg, | ||||
| 										  &l_rte, | ||||
| 										  &l_rtindex, | ||||
| 										  NULL, NULL, | ||||
| 										  &l_namespace); | ||||
|  | ||||
| 		/* | ||||
| @@ -1270,34 +1263,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		sv_namespace_length = list_length(pstate->p_namespace); | ||||
| 		pstate->p_namespace = list_concat(pstate->p_namespace, l_namespace); | ||||
|  | ||||
| 		/* | ||||
| 		 * If we are running MERGE, don't make the other RTEs visible while | ||||
| 		 * parsing the source relation. It mustn't see them. | ||||
| 		 * | ||||
| 		 * Currently, only MERGE passes non-NULL value for right_rte, so we | ||||
| 		 * can safely deduce if we're running MERGE or not by just looking at | ||||
| 		 * the right_rte. If that ever changes, we should look at other means | ||||
| 		 * to find that. | ||||
| 		 */ | ||||
| 		if (right_rte) | ||||
| 		{ | ||||
| 			save_namespace = pstate->p_namespace; | ||||
| 			pstate->p_namespace = NIL; | ||||
| 		} | ||||
|  | ||||
| 		/* And now we can process the RHS */ | ||||
| 		j->rarg = transformFromClauseItem(pstate, j->rarg, | ||||
| 										  &r_rte, | ||||
| 										  &r_rtindex, | ||||
| 										  NULL, NULL, | ||||
| 										  &r_namespace); | ||||
|  | ||||
| 		/* | ||||
| 		 * And now restore the namespace again so that join-quals can see it. | ||||
| 		 */ | ||||
| 		if (right_rte) | ||||
| 			pstate->p_namespace = save_namespace; | ||||
|  | ||||
| 		/* Remove the left-side RTEs from the namespace list again */ | ||||
| 		pstate->p_namespace = list_truncate(pstate->p_namespace, | ||||
| 											sv_namespace_length); | ||||
| @@ -1324,12 +1295,6 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		expandRTE(r_rte, r_rtindex, 0, -1, false, | ||||
| 				  &r_colnames, &r_colvars); | ||||
|  | ||||
| 		if (right_rte) | ||||
| 			*right_rte = r_rte; | ||||
|  | ||||
| 		if (right_rti) | ||||
| 			*right_rti = r_rtindex; | ||||
|  | ||||
| 		/* | ||||
| 		 * Natural join does not explicitly specify columns; must generate | ||||
| 		 * columns to join. Need to run through the list of columns from each | ||||
| @@ -1558,7 +1523,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, | ||||
| 		 * The join RTE itself is always made visible for unqualified column | ||||
| 		 * names.  It's visible as a relation name only if it has an alias. | ||||
| 		 */ | ||||
| 		*fnamespace = lappend(my_namespace, | ||||
| 		*namespace = lappend(my_namespace, | ||||
| 							 makeNamespaceItem(rte, | ||||
| 											   (j->alias != NULL), | ||||
| 											   true, | ||||
|   | ||||
| @@ -485,7 +485,6 @@ assign_collations_walker(Node *node, assign_collations_context *context) | ||||
| 		case T_FromExpr: | ||||
| 		case T_OnConflictExpr: | ||||
| 		case T_SortGroupClause: | ||||
| 		case T_MergeAction: | ||||
| 			(void) expression_tree_walker(node, | ||||
| 										  assign_collations_walker, | ||||
| 										  (void *) &loccontext); | ||||
|   | ||||
| @@ -1818,7 +1818,6 @@ transformSubLink(ParseState *pstate, SubLink *sublink) | ||||
| 		case EXPR_KIND_RETURNING: | ||||
| 		case EXPR_KIND_VALUES: | ||||
| 		case EXPR_KIND_VALUES_SINGLE: | ||||
| 		case EXPR_KIND_MERGE_WHEN_AND: | ||||
| 			/* okay */ | ||||
| 			break; | ||||
| 		case EXPR_KIND_CHECK_CONSTRAINT: | ||||
| @@ -3476,8 +3475,6 @@ ParseExprKindName(ParseExprKind exprKind) | ||||
| 			return "PARTITION BY"; | ||||
| 		case EXPR_KIND_CALL_ARGUMENT: | ||||
| 			return "CALL"; | ||||
| 		case EXPR_KIND_MERGE_WHEN_AND: | ||||
| 			return "MERGE WHEN AND"; | ||||
|  | ||||
| 			/* | ||||
| 			 * There is intentionally no default: case here, so that the | ||||
|   | ||||
| @@ -2277,9 +2277,6 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location) | ||||
| 			/* okay, since we process this like a SELECT tlist */ | ||||
| 			pstate->p_hasTargetSRFs = true; | ||||
| 			break; | ||||
| 		case EXPR_KIND_MERGE_WHEN_AND: | ||||
| 			err = _("set-returning functions are not allowed in WHEN AND conditions"); | ||||
| 			break; | ||||
| 		case EXPR_KIND_CHECK_CONSTRAINT: | ||||
| 		case EXPR_KIND_DOMAIN_CHECK: | ||||
| 			err = _("set-returning functions are not allowed in check constraints"); | ||||
|   | ||||
| @@ -1,654 +0,0 @@ | ||||
| /*------------------------------------------------------------------------- | ||||
|  * | ||||
|  * 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_cte.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 setNamespaceForMergeWhen(ParseState *pstate, | ||||
| 						MergeWhenClause *mergeWhenClause); | ||||
| 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 | ||||
| setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause) | ||||
| { | ||||
| 	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 (mergeWhenClause->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; | ||||
| 	List		   *mergeActionList; | ||||
|  | ||||
| 	/* There can't be any outer WITH to worry about */ | ||||
| 	Assert(pstate->p_ctenamespace == NIL); | ||||
|  | ||||
| 	qry->commandType = CMD_MERGE; | ||||
| 	qry->hasRecursive = false; | ||||
|  | ||||
| 	/* process the WITH clause independently of all else */ | ||||
| 	if (stmt->withClause) | ||||
| 	{ | ||||
| 		if (stmt->withClause->recursive) | ||||
| 			ereport(ERROR, | ||||
| 					(errcode(ERRCODE_SYNTAX_ERROR), | ||||
| 					 errmsg("WITH RECURSIVE is not supported for MERGE statement"))); | ||||
|  | ||||
| 		qry->cteList = transformWithClause(pstate, stmt->withClause); | ||||
| 		qry->hasModifyingCTE = pstate->p_hasModifyingCTE; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Check WHEN clauses for permissions and sanity | ||||
| 	 */ | ||||
| 	is_terminal[0] = false; | ||||
| 	is_terminal[1] = false; | ||||
| 	foreach(l, stmt->mergeWhenClauses) | ||||
| 	{ | ||||
| 		MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l); | ||||
| 		int		when_type = (mergeWhenClause->matched ? 0 : 1); | ||||
|  | ||||
| 		/* | ||||
| 		 * Collect action types so we can check Target permissions | ||||
| 		 */ | ||||
| 		switch (mergeWhenClause->commandType) | ||||
| 		{ | ||||
| 			case CMD_INSERT: | ||||
| 				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 (mergeWhenClause->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. | ||||
| 	 */ | ||||
| 	mergeActionList = NIL; | ||||
| 	foreach(l, stmt->mergeWhenClauses) | ||||
| 	{ | ||||
| 		MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l); | ||||
| 		MergeAction		  *action = makeNode(MergeAction); | ||||
|  | ||||
| 		action->commandType = mergeWhenClause->commandType; | ||||
| 		action->matched = mergeWhenClause->matched; | ||||
|  | ||||
| 		/* | ||||
| 		 * Set namespace for the specific action. This must be done before | ||||
| 		 * analyzing the WHEN quals and the action targetlisst. | ||||
| 		 */ | ||||
| 		setNamespaceForMergeWhen(pstate, mergeWhenClause); | ||||
|  | ||||
| 		/* | ||||
| 		 * 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, mergeWhenClause->condition, | ||||
| 											EXPR_KIND_MERGE_WHEN_AND, "WHEN"); | ||||
|  | ||||
| 		/* | ||||
| 		 * Transform target lists for each INSERT and UPDATE action stmt | ||||
| 		 */ | ||||
| 		switch (action->commandType) | ||||
| 		{ | ||||
| 			case CMD_INSERT: | ||||
| 				{ | ||||
| 					List	   *exprList = NIL; | ||||
| 					ListCell   *lc; | ||||
| 					RangeTblEntry *rte; | ||||
| 					ListCell   *icols; | ||||
| 					ListCell   *attnos; | ||||
| 					List	   *icolumns; | ||||
| 					List	   *attrnos; | ||||
|  | ||||
| 					pstate->p_is_insert = true; | ||||
|  | ||||
| 					icolumns = checkInsertTargets(pstate, | ||||
| 												  mergeWhenClause->cols, | ||||
| 												  &attrnos); | ||||
| 					Assert(list_length(icolumns) == list_length(attrnos)); | ||||
|  | ||||
| 					action->override = mergeWhenClause->override; | ||||
|  | ||||
| 					/* | ||||
| 					 * Handle INSERT much like in transformInsertStmt | ||||
| 					 */ | ||||
| 					if (mergeWhenClause->values == NIL) | ||||
| 					{ | ||||
| 						/* | ||||
| 						 * 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. | ||||
| 						 */ | ||||
|  | ||||
| 						/* | ||||
| 						 * Do basic expression transformation (same as a ROW() | ||||
| 						 * expr, but allow SetToDefault at top level) | ||||
| 						 */ | ||||
| 						exprList = transformExpressionList(pstate, | ||||
| 														   mergeWhenClause->values, | ||||
| 														   EXPR_KIND_VALUES_SINGLE, | ||||
| 														   true); | ||||
|  | ||||
| 						/* Prepare row for assignment to target table */ | ||||
| 						exprList = transformInsertRow(pstate, exprList, | ||||
| 													  mergeWhenClause->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: | ||||
| 				{ | ||||
| 					pstate->p_is_insert = false; | ||||
| 					action->targetList = transformUpdateTargetList(pstate, | ||||
| 																   mergeWhenClause->targetList); | ||||
| 				} | ||||
| 				break; | ||||
| 			case CMD_DELETE: | ||||
| 				break; | ||||
|  | ||||
| 			case CMD_NOTHING: | ||||
| 				action->targetList = NIL; | ||||
| 				break; | ||||
| 			default: | ||||
| 				elog(ERROR, "unknown action in MERGE WHEN clause"); | ||||
| 		} | ||||
|  | ||||
| 		mergeActionList = lappend(mergeActionList, action); | ||||
| 	} | ||||
|  | ||||
| 	qry->mergeActionList = 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; | ||||
| } | ||||
| @@ -728,16 +728,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, const char *colname, | ||||
| 							colname), | ||||
| 					 parser_errposition(pstate, location))); | ||||
|  | ||||
| 		/* In MERGE WHEN AND condition, no system column is allowed except tableOid or OID */ | ||||
| 		if (pstate->p_expr_kind == EXPR_KIND_MERGE_WHEN_AND && | ||||
| 			attnum < InvalidAttrNumber && | ||||
| 			!(attnum == TableOidAttributeNumber || attnum == ObjectIdAttributeNumber)) | ||||
| 			ereport(ERROR, | ||||
| 					(errcode(ERRCODE_INVALID_COLUMN_REFERENCE), | ||||
| 					 errmsg("system column \"%s\" reference in WHEN AND condition is invalid", | ||||
| 							colname), | ||||
| 					 parser_errposition(pstate, location))); | ||||
|  | ||||
| 		if (attnum != InvalidAttrNumber) | ||||
| 		{ | ||||
| 			/* now check to see if column actually is defined */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user