On Fri, Nov 12, 2021 at 02:57:57PM -0300, Alvaro Herrera wrote:
> Here's a new version.  Many of the old complaints have been fixed;

I should've replied to this newer message.

I also read through the code a bit and have some more language fixes, mostly.

I ran sqlsmith for a few hours with no apparent issue - good.

It seems like there's an opened question about how RULES should be handled?

-- 
Justin
>From 119c6c7342ff9410424e94d60dc11b3a5e9d98c9 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryz...@telsasoft.com>
Date: Sat, 13 Nov 2021 15:47:12 -0600
Subject: [PATCH] f!merge code comments

---
 src/backend/commands/trigger.c       |  8 +++----
 src/backend/executor/execMerge.c     | 24 +++++++++----------
 src/backend/optimizer/plan/planner.c |  2 +-
 src/backend/optimizer/plan/setrefs.c |  2 +-
 src/backend/parser/parse_merge.c     | 36 +++++++++++++++-------------
 src/backend/rewrite/rowsecurity.c    |  2 +-
 src/bin/psql/tab-complete.c          |  2 +-
 7 files changed, 39 insertions(+), 37 deletions(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index ea5c276cca..e708e4d185 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3694,9 +3694,9 @@ struct AfterTriggersTableData
 	AfterTriggerEventList after_trig_events;	/* if so, saved list pointer */
 
 	/*
-	 * We maintain separate transaction tables for UPDATE/INSERT/DELETE since
+	 * We maintain separate transition tables for UPDATE/INSERT/DELETE since
 	 * MERGE can run all three actions in a single statement. Note that UPDATE
-	 * needs both old and new transition tables whereas INSERT needs only new
+	 * needs both old and new transition tables whereas INSERT needs only new,
 	 * and DELETE needs only old.
 	 */
 
@@ -5650,8 +5650,8 @@ AfterTriggerGetTransitionTable(int event,
 	bool		insert_new_table = transition_capture->tcs_insert_new_table;
 
 	/*
-	 * For INSERT events NEW should be non-NULL, for DELETE events OLD should
-	 * be non-NULL, whereas for UPDATE events normally both OLD and NEW are
+	 * For INSERT events, NEW should be non-NULL, for DELETE events, OLD should
+	 * be non-NULL, whereas for UPDATE events, normally both OLD and NEW are
 	 * non-NULL.  But for UPDATE events fired for capturing transition tuples
 	 * during UPDATE partition-key row movement, OLD is NULL when the event is
 	 * for a row being inserted, whereas NEW is NULL when the event is for a
diff --git a/src/backend/executor/execMerge.c b/src/backend/executor/execMerge.c
index a9bd103f41..aabe10d14c 100644
--- a/src/backend/executor/execMerge.c
+++ b/src/backend/executor/execMerge.c
@@ -66,9 +66,9 @@ ExecMerge(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 
 	/*
 	 * We run a JOIN between the target relation and the source relation to
-	 * find a set of candidate source rows that has matching row in the target
-	 * table and a set of candidate source rows that does not have matching
-	 * row in the target table. If the join returns us a tuple with target
+	 * find a set of candidate source rows that have a matching row in the target
+	 * table and a set of candidate source rows that does not have a matching
+	 * row in the target table. If the join returns a tuple with the target
 	 * relation's tid set, that implies that the join found a matching row for
 	 * the given source tuple. This case triggers the WHEN MATCHED clause of
 	 * the MERGE. Whereas a NULL in the target relation's ctid column
@@ -122,7 +122,7 @@ ExecMerge(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 	 * XXX Hmmm, what if the updated tuple would now match one that was
 	 * considered NOT MATCHED so far?
 	 *
-	 * A concurrent delete, changes a WHEN MATCHED case to WHEN NOT MATCHED.
+	 * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED.
 	 *
 	 * ExecMergeMatched takes care of following the update chain and
 	 * re-finding the qualifying WHEN MATCHED action, as long as the updated
@@ -140,7 +140,7 @@ ExecMerge(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 	/*
 	 * Either we were dealing with a NOT MATCHED tuple or
 	 * ExecMergeNotMatched() returned "false", indicating the previously
-	 * MATCHED tuple is no longer a matching tuple.
+	 * MATCHED tuple no longer matches.
 	 */
 	if (!matched)
 		ExecMergeNotMatched(mtstate, resultRelInfo, estate, slot);
@@ -223,7 +223,7 @@ lmerge_matched:
 		/*
 		 * Test condition, if any
 		 *
-		 * In the absence of a condition we perform the action unconditionally
+		 * In the absence of any condition, we perform the action unconditionally
 		 * (no need to check separately since ExecQual() will return true if
 		 * there are no conditions to evaluate).
 		 */
@@ -231,14 +231,14 @@ lmerge_matched:
 			continue;
 
 		/*
-		 * Check if the existing target tuple meet the USING checks of
+		 * Check if the existing target tuple meets the USING checks of
 		 * UPDATE/DELETE RLS policies. If those checks fail, we throw an
 		 * error.
 		 *
 		 * The WITH CHECK quals are applied in ExecUpdate() and hence we need
 		 * not do anything special to handle them.
 		 *
-		 * NOTE: We must do this after WHEN quals are evaluated so that we
+		 * NOTE: We must do this after WHEN quals are evaluated, so that we
 		 * check policies only when they matter.
 		 */
 		if (resultRelInfo->ri_WithCheckOptions)
@@ -340,7 +340,7 @@ lmerge_matched:
 					 *
 					 * If the current tuple was concurrently updated, then we
 					 * must run the EvalPlanQual() with the new version of the
-					 * tuple. If EvalPlanQual() does not return a tuple then
+					 * tuple. If EvalPlanQual() does not return a tuple, then
 					 * we switch to the NOT MATCHED list of actions. If it
 					 * does return a tuple and the join qual is still
 					 * satisfied, then we just need to recheck the MATCHED
@@ -496,7 +496,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 	ListCell   *l;
 
 	/*
-	 * For INSERT actions, root relation's merge action is OK since the
+	 * For INSERT actions, the root relation's merge action is OK since the
 	 * INSERT's targetlist and the WHEN conditions can only refer to the
 	 * source relation and hence it does not matter which result relation we
 	 * work with.
@@ -508,7 +508,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 
 	/*
 	 * Make source tuple available to ExecQual and ExecProject. We don't need
-	 * the target tuple since the WHEN quals and the targetlist can't refer to
+	 * the target tuple, since the WHEN quals and the targetlist can't refer to
 	 * the target columns.
 	 */
 	econtext->ecxt_scantuple = NULL;
@@ -523,7 +523,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo,
 		/*
 		 * Test condition, if any
 		 *
-		 * In the absence of a condition we perform the action unconditionally
+		 * In the absence of a condition, we perform the action unconditionally
 		 * (no need to check separately since ExecQual() will return true if
 		 * there are no conditions to evaluate).
 		 */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 159c38ae91..89104fccb6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1811,7 +1811,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 
 						/*
 						 * Copy MergeActions and translate stuff that
-						 * reference attribute numbers.
+						 * references attribute numbers.
 						 */
 						foreach(l, parse->mergeActionList)
 						{
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 476583a85e..f627b832f1 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1035,7 +1035,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 				 * a right join between the target relation and the source
 				 * relation (which could be a plain relation or a subquery).
 				 * The INSERT and UPDATE actions of the MERGE statement
-				 * requires access to the columns from the source relation. We
+				 * require access to the columns from the source relation. We
 				 * arrange things so that the source relation attributes are
 				 * available as INNER_VAR and the target relation attributes
 				 * are available from the scan tuple.
diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index e183147ea8..aa28293407 100644
--- a/src/backend/parser/parse_merge.c
+++ b/src/backend/parser/parse_merge.c
@@ -47,7 +47,7 @@ static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
  *	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
+ *	A special targetlist comprised 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.
@@ -86,14 +86,14 @@ transformMergeJoinClause(ParseState *pstate, Node *merge,
 	 * 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
+	 * execution is later set up, 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
+	 * We also do not want to resolve any references from the left side 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.
@@ -252,12 +252,13 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 					 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
+	 * ,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
@@ -275,17 +276,18 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	 * 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,
+	 * As top-level statements, INSERT, UPDATE and DELETE have a Query, whereas
+	 * for 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.
+	 * ---
 	 */
 
 	/*
-	 * Setup the MERGE target table.
+	 * Set up the MERGE target table.
 	 */
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
 										 stmt->relation->inh,
@@ -309,7 +311,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	 * 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
+	 * 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.
 	 */
@@ -322,7 +324,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	 * 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().
+	 * A special mergeSourceTargetList is set up by transformMergeJoinClause().
 	 * It refers to all the attributes output by the join.
 	 */
 	transformMergeJoinClause(pstate, (Node *) joinexpr,
@@ -343,8 +345,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 	/*
 	 * 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))
+	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")));
@@ -361,7 +363,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 				 errmsg("MERGE is not supported for relations with rules")));
 
 	/*
-	 * We now have a good query shape, so now look at the when conditions and
+	 * 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.
@@ -391,7 +393,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 		setNamespaceForMergeWhen(pstate, mergeWhenClause);
 
 		/*
-		 * Transform the when condition.
+		 * 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
diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c
index 1c9aa22422..b608553acd 100644
--- a/src/backend/rewrite/rowsecurity.c
+++ b/src/backend/rewrite/rowsecurity.c
@@ -391,7 +391,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
 	 *
 	 * We don't push the UPDATE/DELETE USING quals to the RTE because we don't
 	 * really want to apply them while scanning the relation since we don't
-	 * know whether we will be doing a UPDATE or a DELETE at the end. We apply
+	 * know whether we will be doing an UPDATE or a DELETE at the end. We apply
 	 * the respective policy once we decide the final action on the target
 	 * tuple.
 	 *
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index a2d4755a6f..650a7fb5d8 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -536,7 +536,7 @@ static const SchemaQuery Query_for_list_of_updatables = {
 /* Relations supporting MERGE */
 static const SchemaQuery Query_for_list_of_mergetargets = {
 	/* min_server_version */
-	110000,
+	150000,
 	/* catname */
 	"pg_catalog.pg_class c",
 	/* selcondition */
-- 
2.17.0

Reply via email to