>From e47f1315277691f6562a6229a81556ceaed461b7 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Thu, 25 Aug 2016 17:49:59 +0900
Subject: [PATCH 5/9] Refactor optimizer's inheritance set expansion code.

Currently, a inheritance set is flattened upon expansion so that
AppendRelInfos so formed do not preserve the immediate parent-child
relationship which could be useful information in certain optimization
scenarios.  That is especially true for partitioned tables which are
fashioned as inheritance hierarchies.

Because certain restrictions (such as multiple inheritance) that prevent
regular inheritance expansion to be done recursively do not hold for
partitioned table hierarchies, do the partitioned table inheritance set
expansion recursively.
---
 src/backend/optimizer/prep/prepunion.c |  278 ++++++++++++++++++++++---------
 src/backend/optimizer/util/plancat.c   |    9 +-
 2 files changed, 204 insertions(+), 83 deletions(-)

diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b714783..193b2c9 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -111,6 +111,14 @@ static Node *adjust_appendrel_attrs_mutator(Node *node,
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
 static List *adjust_inherited_tlist(List *tlist,
 					   AppendRelInfo *context);
+static List *expand_inherited_rte_internal(PlannerInfo *root, RangeTblEntry *rte,
+							 Index rti, PlanRowMark *oldrc,
+							 LOCKMODE lockmode, bool flatten);
+static AppendRelInfo *process_one_child_table(PlannerInfo *root,
+						RangeTblEntry *parentRTE, Index parentRTindex,
+						Relation parentrel, Relation childrel,
+						PlanRowMark *parent_rc, bool inh,
+						RangeTblEntry **childRTE, Index *childRTindex);
 
 
 /*
@@ -1324,7 +1332,10 @@ expand_inherited_tables(PlannerInfo *root)
 
 	/*
 	 * expand_inherited_rtentry may add RTEs to parse->rtable; there is no
-	 * need to scan them since they can't have inh=true.  So just scan as far
+	 * need to scan them here since they can't normally have inh=true.  If
+	 * the inheritance set represents a partitioned table, some newly added
+	 * RTEs will break the above rule if they are partitioned tables
+	 * themselves, but they are expanded recursively.  So just scan as far
 	 * as the original end of the rtable list.
 	 */
 	nrtes = list_length(root->parse->rtable);
@@ -1347,9 +1358,11 @@ expand_inherited_tables(PlannerInfo *root)
  *		"inh" flag to prevent later code from looking for AppendRelInfos.
  *
  * Note that the original RTE is considered to represent the whole
- * inheritance set.  The first of the generated RTEs is an RTE for the same
- * table, but with inh = false, to represent the parent table in its role
- * as a simple member of the inheritance set.
+ * inheritance set.  If the RTE represents a partitioned table, inheritance
+ * set is expanded recursively.  The first of the generated RTEs is an RTE
+ * for the same table, but with inh = false, to represent the parent table
+ * in its role as a simple member of the inheritance set.  The same applies
+ * to each individual inheritance set in the recursive expansion case.
  *
  * A childless table is never considered to be an inheritance set; therefore
  * a parent RTE must always have at least two associated AppendRelInfos.
@@ -1360,11 +1373,8 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	Query	   *parse = root->parse;
 	Oid			parentOID;
 	PlanRowMark *oldrc;
-	Relation	oldrelation;
 	LOCKMODE	lockmode;
-	List	   *inhOIDs;
 	List	   *appinfos;
-	ListCell   *l;
 
 	/* Does RT entry allow inheritance? */
 	if (!rte->inh)
@@ -1405,19 +1415,65 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	else
 		lockmode = AccessShareLock;
 
-	/* Scan for all members of inheritance set, acquire needed locks */
-	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
+	/* Do not flatten the inheritance hierarchy if partitioned table */
+	if (rte->relkind != RELKIND_PARTITIONED_TABLE)
+		appinfos = expand_inherited_rte_internal(root, rte, rti, oldrc,
+												 lockmode, true);
+	else
+		appinfos = expand_inherited_rte_internal(root, rte, rti, oldrc,
+												 lockmode, false);
+
+	/* Add to root->append_rel_list */
+	root->append_rel_list = list_concat(root->append_rel_list, appinfos);
+}
+
+/*
+ * expand_inherited_rte_internal
+ *		Expand an inheritance set in either non-recursive (flatten=true) or
+ *		recursive (flatten=false) manner.
+ *
+ * A inheritance hierarchy is not flttened if it represents a partitioned
+ * table.  This allows later planning steps to apply any partitioning
+ * related optimizations in suitable manner.
+ */
+static List *
+expand_inherited_rte_internal(PlannerInfo *root, RangeTblEntry *rte,
+							  Index rti, PlanRowMark *oldrc,
+							  LOCKMODE lockmode, bool flatten)
+{
+	Oid			parentOID;
+	Relation	oldrelation;
+	List	   *inhOIDs;
+	List	   *appinfos = NIL;
+	ListCell   *l;
+	bool		has_descendents;
+
+	Assert(rte->rtekind == RTE_RELATION);
+	parentOID = rte->relid;
 
 	/*
-	 * Check that there's at least one descendant, else treat as no-child
+	 * Get the list of inheritors.
+	 *
+	 * Also check that there's at least one descendant, else treat as no-child
 	 * case.  This could happen despite above has_subclass() check, if table
 	 * once had a child but no longer does.
 	 */
-	if (list_length(inhOIDs) < 2)
+	if (flatten)
+	{
+		inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
+		has_descendents = list_length(inhOIDs) >= 2;
+	}
+	else
+	{
+		inhOIDs = find_inheritance_children(parentOID, lockmode);
+		has_descendents = list_length(inhOIDs) >= 1;
+	}
+
+	if (!has_descendents)
 	{
 		/* Clear flag before returning */
 		rte->inh = false;
-		return;
+		return NIL;
 	}
 
 	/*
@@ -1434,15 +1490,24 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	 */
 	oldrelation = heap_open(parentOID, NoLock);
 
+	/*
+	 * Process parent relation in its role as inheritance set member; remember
+	 * that parent table OID is not in inhOIDs if we did not flatten the
+	 * inheritance tree.
+	 */
+	if (!flatten)
+		appinfos = list_make1(process_one_child_table(root, rte, rti,
+													  oldrelation, oldrelation,
+													  oldrc, false,
+													  NULL, NULL));
+
 	/* Scan the inheritance set and expand it */
-	appinfos = NIL;
 	foreach(l, inhOIDs)
 	{
 		Oid			childOID = lfirst_oid(l);
 		Relation	newrelation;
 		RangeTblEntry *childrte;
 		Index		childRTindex;
-		AppendRelInfo *appinfo;
 
 		/* Open rel if needed; we already have required locks */
 		if (childOID != parentOID)
@@ -1463,75 +1528,29 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 		}
 
 		/*
-		 * Build an RTE for the child, and attach to query's rangetable list.
-		 * We copy most fields of the parent's RTE, but replace relation OID
-		 * and relkind, and set inh = false.  Also, set requiredPerms to zero
-		 * since all required permissions checks are done on the original RTE.
-		 */
-		childrte = copyObject(rte);
-		childrte->relid = childOID;
-		childrte->relkind = newrelation->rd_rel->relkind;
-		childrte->inh = false;
-		childrte->requiredPerms = 0;
-		parse->rtable = lappend(parse->rtable, childrte);
-		childRTindex = list_length(parse->rtable);
-
-		/*
-		 * Build an AppendRelInfo for this parent and child.
-		 */
-		appinfo = makeNode(AppendRelInfo);
-		appinfo->parent_relid = rti;
-		appinfo->child_relid = childRTindex;
-		appinfo->parent_reltype = oldrelation->rd_rel->reltype;
-		appinfo->child_reltype = newrelation->rd_rel->reltype;
-		make_inh_translation_list(oldrelation, newrelation, childRTindex,
-								  &appinfo->translated_vars);
-		appinfo->parent_reloid = parentOID;
-		appinfos = lappend(appinfos, appinfo);
-
-		/*
-		 * Translate the column permissions bitmaps to the child's attnums (we
-		 * have to build the translated_vars list before we can do this). But
-		 * if this is the parent table, leave copyObject's result alone.
+		 * process_one_child_table() performs the following actions for the
+		 * child table:
 		 *
-		 * Note: we need to do this even though the executor won't run any
-		 * permissions checks on the child RTE.  The insertedCols/updatedCols
-		 * bitmaps may be examined for trigger-firing purposes.
+		 * 1. add a new RTE to the query rtable,
+		 * 2. builds a PlanRowMark and adds to the root->rowMarks list
+		 * 3. builds and returns AppendRelInfo for parent-child pair
 		 */
-		if (childOID != parentOID)
+		appinfos = lappend(appinfos,
+						   process_one_child_table(root, rte, rti,
+												   oldrelation, newrelation,
+												   oldrc, false,
+												   &childrte, &childRTindex));
+
+		/* Recurse if we did not flatten the inheritance tree */
+		if (!flatten && has_subclass(childOID))
 		{
-			childrte->selectedCols = translate_col_privs(rte->selectedCols,
-												   appinfo->translated_vars);
-			childrte->insertedCols = translate_col_privs(rte->insertedCols,
-												   appinfo->translated_vars);
-			childrte->updatedCols = translate_col_privs(rte->updatedCols,
-												   appinfo->translated_vars);
+			Assert(childrte->relkind == RELKIND_PARTITIONED_TABLE);
+			childrte->inh = true;
+			appinfos = list_concat(appinfos,
+							   expand_inherited_rte_internal(root, childrte,
+										childRTindex, oldrc, lockmode, flatten));
 		}
 
-		/*
-		 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
-		 */
-		if (oldrc)
-		{
-			PlanRowMark *newrc = makeNode(PlanRowMark);
-
-			newrc->rti = childRTindex;
-			newrc->prti = rti;
-			newrc->rowmarkId = oldrc->rowmarkId;
-			/* Reselect rowmark type, because relkind might not match parent */
-			newrc->markType = select_rowmark_type(childrte, oldrc->strength);
-			newrc->allMarkTypes = (1 << newrc->markType);
-			newrc->strength = oldrc->strength;
-			newrc->waitPolicy = oldrc->waitPolicy;
-			newrc->isParent = false;
-
-			/* Include child's rowmark type in parent's allMarkTypes */
-			oldrc->allMarkTypes |= newrc->allMarkTypes;
-
-			root->rowMarks = lappend(root->rowMarks, newrc);
-		}
-
-		/* Close child relations, but keep locks */
 		if (childOID != parentOID)
 			heap_close(newrelation, NoLock);
 	}
@@ -1547,11 +1566,108 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	{
 		/* Clear flag before returning */
 		rte->inh = false;
-		return;
+		return NIL;
 	}
+	return appinfos;
+}
 
-	/* Otherwise, OK to add to root->append_rel_list */
-	root->append_rel_list = list_concat(root->append_rel_list, appinfos);
+/*
+ * process_one_child_table
+ *		Process one child table in context of inheritance expansion for a
+ *		query
+ *
+ * *childRTE & *childRTindex are output variables when non-NULL.
+ */
+static AppendRelInfo *
+process_one_child_table(PlannerInfo *root,
+						RangeTblEntry *parentRTE, Index parentRTindex,
+						Relation parentrel, Relation childrel,
+						PlanRowMark *parent_rc, bool inh,
+						RangeTblEntry **childRTE, Index *childRTindex)
+{
+	Query  *parse = root->parse;
+	Oid		parentOID = RelationGetRelid(parentrel),
+			childOID = RelationGetRelid(childrel);
+	RangeTblEntry  *newrte;
+	Index			newrti;
+	AppendRelInfo  *appinfo;
+
+	/*
+	 * Build an RTE for the child, and attach to query's rangetable list.
+	 * We copy most fields of the parent's RTE, but replace relation OID
+	 * and relkind, and set inh as requested.  Also, set requiredPerms to
+	 * zero since all required permissions checks are done on the original
+	 * RTE.
+	 */
+	newrte = copyObject(parentRTE);
+	newrte->relid = RelationGetRelid(childrel);
+	newrte->relkind = childrel->rd_rel->relkind;
+	newrte->inh = inh;
+	newrte->requiredPerms = 0;
+	parse->rtable = lappend(parse->rtable, newrte);
+	newrti = list_length(parse->rtable);
+
+	/* Return the child table RT entry and index if requested */
+	if (childRTE)
+		*childRTE = newrte;
+	if (childRTindex)
+		*childRTindex = newrti;
+
+	/*
+	 * Build an AppendRelInfo for this parent and child.
+	 */
+	appinfo = makeNode(AppendRelInfo);
+	appinfo->parent_relid = parentRTindex;
+	appinfo->child_relid = newrti;
+	appinfo->parent_reltype = parentrel->rd_rel->reltype;
+	appinfo->child_reltype = childrel->rd_rel->reltype;
+	make_inh_translation_list(parentrel, childrel, newrti,
+							  &appinfo->translated_vars);
+	appinfo->parent_reloid = parentOID;
+
+	/*
+	 * Translate the column permissions bitmaps to the child's attnums (we
+	 * have to build the translated_vars list before we can do this). But
+	 * if this is the parent table, leave copyObject's result alone.
+	 *
+	 * Note: we need to do this even though the executor won't run any
+	 * permissions checks on the child RTE.  The insertedCols/updatedCols
+	 * bitmaps may be examined for trigger-firing purposes.
+	 */
+	if (childOID != parentOID)
+	{
+		newrte->selectedCols = translate_col_privs(parentRTE->selectedCols,
+											   appinfo->translated_vars);
+		newrte->insertedCols = translate_col_privs(parentRTE->insertedCols,
+											   appinfo->translated_vars);
+		newrte->updatedCols = translate_col_privs(parentRTE->updatedCols,
+											   appinfo->translated_vars);
+	}
+
+	/*
+	 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
+	 */
+	if (parent_rc)
+	{
+		PlanRowMark *newrc = makeNode(PlanRowMark);
+
+		newrc->rti = newrti;
+		newrc->prti = parentRTindex;
+		newrc->rowmarkId = parent_rc->rowmarkId;
+		/* Reselect rowmark type, because relkind might not match parent */
+		newrc->markType = select_rowmark_type(newrte, parent_rc->strength);
+		newrc->allMarkTypes = (1 << newrc->markType);
+		newrc->strength = parent_rc->strength;
+		newrc->waitPolicy = parent_rc->waitPolicy;
+		newrc->isParent = false;
+
+		/* Include child's rowmark type in parent's allMarkTypes */
+		parent_rc->allMarkTypes |= newrc->allMarkTypes;
+
+		root->rowMarks = lappend(root->rowMarks, newrc);
+	}
+
+	return appinfo;
 }
 
 /*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5d18206..8ecc116 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1287,8 +1287,13 @@ relation_excluded_by_constraints(PlannerInfo *root,
 	if (predicate_refuted_by(safe_restrictions, safe_restrictions))
 		return true;
 
-	/* Only plain relations have constraints */
-	if (rte->rtekind != RTE_RELATION || rte->inh)
+	/*
+	 * Only plain relations have constraints.  We represent a partitioned
+	 * table append member as its own append relation and hence would have
+	 * set rte->inh in that case.
+	 */
+	if (rte->rtekind != RTE_RELATION ||
+		(rte->inh && rte->relkind != RELKIND_PARTITIONED_TABLE))
 		return false;
 
 	/*
-- 
1.7.1

