From cbcda1def53d637ad94fde463f2cfe4d5c55d3ce Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@cn.fujitsu.com>
Date: Tue, 9 Mar 2021 14:48:59 +0800
Subject: [PATCH] foreign key

Extent the parallel-safety check to treat the following cases parallel restricted:
1) foreign key and primary key are on the same table(INSERT's target table).
  (referenced and referencing are the same table)
2) referenced and referencing table are both partition of INSERT's target table.


---
 src/backend/optimizer/util/clauses.c | 77 +++++++++++++++++++++++++++++++++---
 1 file changed, 72 insertions(+), 5 deletions(-)

diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f54268c..a1928d9 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -100,6 +100,7 @@ typedef struct
 	RangeTblEntry *target_rte;	/* query's target relation if any */
 	CmdType		command_type;	/* query's command type */
 	PlannerGlobal *planner_global;	/* global info for planner invocation */
+	List	   *pk_rels;		/* OIDs of relations referenced by target relation */
 } max_parallel_hazard_context;
 
 static bool contain_agg_clause_walker(Node *node, void *context);
@@ -582,6 +583,7 @@ max_parallel_hazard(Query *parse, PlannerGlobal *glob)
 		rt_fetch(parse->resultRelation, parse->rtable) : NULL;
 	context.command_type = parse->commandType;
 	context.planner_global = glob;
+	context.pk_rels = NIL;
 	(void) max_parallel_hazard_walker((Node *) parse, &context);
 
 	return context.max_hazard;
@@ -872,7 +874,7 @@ static bool
 target_rel_max_parallel_hazard(max_parallel_hazard_context *context)
 {
 	bool		max_hazard_found;
-
+	ListCell   *lc;
 	Relation	targetRel;
 
 	/*
@@ -885,6 +887,35 @@ target_rel_max_parallel_hazard(max_parallel_hazard_context *context)
 															  context->command_type,
 															  context);
 
+	/*
+	 * Check if target relation is one of PK relations.
+	 */
+	if (!max_hazard_found && context->max_hazard == PROPARALLEL_SAFE)
+	{
+		if (list_member_oid(context->pk_rels, context->target_rte->relid))
+		{
+			max_hazard_found = max_parallel_hazard_test(PROPARALLEL_RESTRICTED,
+														context);
+		}
+	}
+
+	/*
+	 * check if any pk relation is partition of the target table.
+	 */
+	if (context->max_hazard == PROPARALLEL_SAFE)
+	{
+		foreach(lc, context->pk_rels)
+		{
+			if (list_member_oid(context->planner_global->partitionOids,
+							   lfirst_oid(lc)))
+			{
+				max_hazard_found = max_parallel_hazard_test(PROPARALLEL_RESTRICTED,
+															context);
+				break;
+			}
+		}
+	}
+
 	table_close(targetRel, NoLock);
 
 	return max_hazard_found;
@@ -974,15 +1005,51 @@ target_rel_trigger_max_parallel_hazard(TriggerDesc *trigdesc,
 
 		/*
 		 * If the trigger type is RI_TRIGGER_FK, this indicates a FK exists in
-		 * the relation, and this would result in creation of new CommandIds
-		 * on insert/update and this isn't supported in a parallel
-		 * worker (but is safe in the parallel leader).
+		 * the relation, this should result in creation of new CommandIds if
+		 * PK relation is modified.
+		 *
+		 * However, creation of new CommandIds is not supported in a
+		 * parallel worker(but is safe in the parallel leader). So, treat all
+		 * scenarios that may modify PK relation as parallel-restricted.
 		 */
 		trigtype = RI_FKey_trigger_type(trigger->tgfoid);
 		if (trigtype == RI_TRIGGER_FK)
 		{
-			if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
+			HeapTuple	tup;
+			Form_pg_constraint conForm;
+			Oid constraintOid;
+
+			constraintOid = trigger->tgconstraint;
+
+			/*
+			 * Fetch the pg_constraint row so we can fill in the entry.
+			 */
+			tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
+			if (!HeapTupleIsValid(tup)) /* should not happen */
+				elog(ERROR, "cache lookup failed for constraint %u",
+					constraintOid);
+
+			conForm = (Form_pg_constraint) GETSTRUCT(tup);
+			if (conForm->contype != CONSTRAINT_FOREIGN) /* should not happen */
+				elog(ERROR, "constraint %u is not a foreign key constraint",
+					constraintOid);
+
+			/*
+			 * If FK relation and PK relation are not the same,
+			 * they could be partitions of the same table.
+			 * Remember Oids of PK relation for the later check.
+			 */
+			if (conForm->confrelid != conForm->conrelid)
+				context->pk_rels = lappend_oid(context->pk_rels, conForm->confrelid);
+
+			/*
+			 * If FK relation and PK relation are the same, the PK relation
+			 * could be modfied.
+			 */
+			else if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
 				return true;
+
+			ReleaseSysCache(tup);
 		}
 	}
 
-- 
2.7.2.windows.1

