From 6ed57067316b788a1d76b7555f2f6c4835b43e04 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <bossartn@amazon.com>
Date: Wed, 5 Sep 2018 14:43:22 +0000
Subject: [PATCH v9 3/7] Add skip_locked argument to find_all_inheritors().

---
 contrib/sepgsql/dml.c                  |  2 +-
 src/backend/catalog/pg_inherits.c      | 30 +++++++++++++++++++++++++++---
 src/backend/commands/analyze.c         |  2 +-
 src/backend/commands/publicationcmds.c |  2 +-
 src/backend/commands/tablecmds.c       | 16 ++++++++--------
 src/backend/commands/trigger.c         |  2 +-
 src/backend/commands/vacuum.c          |  2 +-
 src/backend/executor/execPartition.c   |  2 +-
 src/backend/optimizer/prep/prepunion.c |  2 +-
 src/backend/partitioning/partbounds.c  |  2 +-
 src/backend/tcop/utility.c             |  3 ++-
 src/include/catalog/pg_inherits.h      |  2 +-
 12 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 9bdbd7b60f..bb7713380a 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -330,7 +330,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
 		if (!rte->inh)
 			tableIds = list_make1_oid(rte->relid);
 		else
-			tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
+			tableIds = find_all_inheritors(rte->relid, NoLock, NULL, false);
 
 		foreach(li, tableIds)
 		{
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 1b936123c5..a64037a958 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -180,10 +180,12 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode, bool skip_locked,
  * The specified lock type is acquired on all child relations (but not on the
  * given rel; caller should already have locked it).  If lockmode is NoLock
  * then no locks are acquired, but caller must beware of race conditions
- * against possible DROPs of child relations.
+ * against possible DROPs of child relations.  If skip_locked is true and a
+ * child relation can not be locked immediately without waiting, both the
+ * returned OID list and *numparents will be set to NIL.
  */
 List *
-find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
+find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents, bool skip_locked)
 {
 	/* hash table for O(1) rel_oid -> rel_numparents cell lookup */
 	HTAB	   *seen_rels;
@@ -191,6 +193,7 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
 	List	   *rels_list,
 			   *rel_numparents;
 	ListCell   *l;
+	bool		skipped = false;
 
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
@@ -219,7 +222,7 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
 		ListCell   *lc;
 
 		/* Get the direct children of this rel */
-		currentchildren = find_inheritance_children(currentrel, lockmode, false, NULL);
+		currentchildren = find_inheritance_children(currentrel, lockmode, skip_locked, &skipped);
 
 		/*
 		 * Add to the queue only those children not already seen. This avoids
@@ -248,6 +251,27 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
 				hash_entry->numparents_cell = rel_numparents->tail;
 			}
 		}
+
+		if (skipped)
+			break;
+	}
+
+	/* if a child relation was skipped, set the return lists to NIL */
+	if (skipped)
+	{
+		/* release all locks */
+		foreach(l, rels_list)
+		{
+			Oid			currentrel = lfirst_oid(l);
+
+			if (currentrel != parentrelId)
+				UnlockRelationOid(currentrel, lockmode);
+		}
+
+		list_free(rel_numparents);
+		list_free(rels_list);
+		rel_numparents = NIL;
+		rels_list = NIL;
 	}
 
 	if (numparents)
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index b6b78527f4..dc73714819 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1308,7 +1308,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
 	 * the children.
 	 */
 	tableOIDs =
-		find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL);
+		find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL, false);
 
 	/*
 	 * Check that there's at least one descendant, else fail.  This could
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 6f7762a906..a5e01ea61e 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -530,7 +530,7 @@ OpenTableList(List *tables)
 			List	   *children;
 
 			children = find_all_inheritors(myrelid, ShareUpdateExclusiveLock,
-										   NULL);
+										   NULL, false);
 
 			foreach(child, children)
 			{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7520de1b56..693cffb1f0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1374,7 +1374,7 @@ ExecuteTruncate(TruncateStmt *stmt)
 			ListCell   *child;
 			List	   *children;
 
-			children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL);
+			children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL, false);
 
 			foreach(child, children)
 			{
@@ -2797,7 +2797,7 @@ renameatt_internal(Oid myrelid,
 		 * outside the inheritance hierarchy being processed.
 		 */
 		child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
-										 &child_numparents);
+										 &child_numparents, false);
 
 		/*
 		 * find_all_inheritors does the recursive search of the inheritance
@@ -3007,7 +3007,7 @@ rename_constraint_internal(Oid myrelid,
 					   *li;
 
 			child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
-											 &child_numparents);
+											 &child_numparents, false);
 
 			forboth(lo, child_oids, li, child_numparents)
 			{
@@ -5067,7 +5067,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
 		ListCell   *child;
 		List	   *children;
 
-		children = find_all_inheritors(relid, lockmode, NULL);
+		children = find_all_inheritors(relid, lockmode, NULL, false);
 
 		/*
 		 * find_all_inheritors does the recursive search of the inheritance
@@ -8048,7 +8048,7 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
 			 */
 			if (!recursing && !con->connoinherit)
 				children = find_all_inheritors(RelationGetRelid(rel),
-											   lockmode, NULL);
+											   lockmode, NULL, false);
 
 			/*
 			 * For CHECK constraints, we must ensure that we only mark the
@@ -9255,7 +9255,7 @@ ATPrepAlterColumnType(List **wqueue,
 		ListCell   *child;
 		List	   *children;
 
-		children = find_all_inheritors(relid, lockmode, NULL);
+		children = find_all_inheritors(relid, lockmode, NULL, false);
 
 		/*
 		 * find_all_inheritors does the recursive search of the inheritance
@@ -11479,7 +11479,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 	 * We use weakest lock we can on child's children, namely AccessShareLock.
 	 */
 	children = find_all_inheritors(RelationGetRelid(child_rel),
-								   AccessShareLock, NULL);
+								   AccessShareLock, NULL, false);
 
 	if (list_member_oid(children, RelationGetRelid(parent_rel)))
 		ereport(ERROR,
@@ -14171,7 +14171,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
 	 * weaker lock now and the stronger one only when needed.
 	 */
 	attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
-											 AccessExclusiveLock, NULL);
+											 AccessExclusiveLock, NULL, false);
 	if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_TABLE),
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index f2617cfb1d..806c7a271c 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -364,7 +364,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 		rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
 	if (partition_recurse)
 		list_free(find_all_inheritors(RelationGetRelid(rel),
-									  ShareRowExclusiveLock, NULL));
+									  ShareRowExclusiveLock, NULL, false));
 
 	/* Compute tgtype */
 	TRIGGER_CLEAR_TYPE(tgtype);
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index fc1081663c..764559f043 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -567,7 +567,7 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
 		 */
 		if (include_parts)
 		{
-			List	   *part_oids = find_all_inheritors(relid, NoLock, NULL);
+			List	   *part_oids = find_all_inheritors(relid, NoLock, NULL, false);
 			ListCell   *part_lc;
 
 			foreach(part_lc, part_oids)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 1a9943c3aa..6bb0b1ba14 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -89,7 +89,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
 	 * Get the information about the partition tree after locking all the
 	 * partitions.
 	 */
-	(void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL);
+	(void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL, false);
 	proute = (PartitionTupleRouting *) palloc0(sizeof(PartitionTupleRouting));
 	proute->partition_dispatch_info =
 		RelationGetPartitionDispatchInfo(rel, &proute->num_dispatch,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 690b6bbab7..4f9305d2ba 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1563,7 +1563,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 		lockmode = AccessShareLock;
 
 	/* Scan for all members of inheritance set, acquire needed locks */
-	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
+	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL, false);
 
 	/*
 	 * Check that there's at least one descendant, else treat as no-child
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 9015a05d32..0e7b627793 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -629,7 +629,7 @@ check_default_partition_contents(Relation parent, Relation default_rel,
 	 */
 	if (default_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 		all_parts = find_all_inheritors(RelationGetRelid(default_rel),
-										AccessExclusiveLock, NULL);
+										AccessExclusiveLock, NULL, false);
 	else
 		all_parts = list_make1_oid(RelationGetRelid(default_rel));
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b5804f64ad..f2c9b46c10 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1325,7 +1325,8 @@ ProcessUtilitySlow(ParseState *pstate,
 						ListCell   *lc;
 						List	   *inheritors = NIL;
 
-						inheritors = find_all_inheritors(relid, lockmode, NULL);
+						inheritors = find_all_inheritors(relid, lockmode,
+															NULL, false);
 						foreach(lc, inheritors)
 						{
 							char		relkind = get_rel_relkind(lfirst_oid(lc));
diff --git a/src/include/catalog/pg_inherits.h b/src/include/catalog/pg_inherits.h
index a89b3f44a5..ccbf756ce5 100644
--- a/src/include/catalog/pg_inherits.h
+++ b/src/include/catalog/pg_inherits.h
@@ -47,7 +47,7 @@ typedef FormData_pg_inherits *Form_pg_inherits;
 extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode,
 						  bool skip_locked, bool *skipped);
 extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
-					List **parents);
+					List **parents, bool skip_locked);
 extern bool has_subclass(Oid relationId);
 extern bool has_superclass(Oid relationId);
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
-- 
2.16.2

