From e47a4f83a69948cfdaba26914079009696cbeccd Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 27 Jul 2016 15:47:39 +0900
Subject: [PATCH 6/8] Introduce a PartitionTreeNode data structure.

It encapsulates the tree structure of a partition hierarchy which can be
arbitrarily deeply nested.  Every node in the tree represents a partitioned
table.  The only currently envisioned application is for tuple-routing.
---
 src/backend/catalog/partition.c |  231 +++++++++++++++++++++++++++++++++++++++
 src/include/catalog/partition.h |    6 +
 2 files changed, 237 insertions(+), 0 deletions(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 109d391..70bfd88 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -110,6 +110,61 @@ typedef struct PartitionInfoData
 	PartitionRangeInfo	   *range;		/* range partition info */
 } PartitionInfoData;
 
+/*
+ * PartitionKeyExecInfo
+ *
+ *		This struct holds the information needed to extract partition
+ *		column values from a heap tuple.
+ *
+ *		Key					copy of the rd_partkey of rel
+ *		ExpressionState		exec state for expressions, or NIL if none
+ */
+typedef struct PartitionKeyExecInfo
+{
+	NodeTag			type;
+	PartitionKey	pi_Key;
+	List		   *pi_ExpressionState;	/* list of ExprState */
+} PartitionKeyExecInfo;
+
+/*
+ * Partition tree node (corresponding to one partitioned table in the
+ * partition tree)
+ *
+ *	pkinfo					PartitionKey executor state
+ *
+ *	pdesc					Info about immediate partitions (see
+ *							PartitionDescData)
+ *
+ *	index					If a partition ourselves, index in the parent's
+ *							partition array
+ *
+ *	num_leaf_partitions		Number of leaf partitions in the partition
+ *							tree rooted at this node
+ *
+ *	offset					0-based index of the first leaf partition
+ *							in the partition tree rooted at this node
+ *
+ *	downlink				Link to our leftmost child node (ie, corresponding
+ *							to first of our partitions that is itself
+ *							partitioned)
+ *
+ *	next					Link to the right sibling node on a given level
+ *							(ie, corresponding to the next partition on the same
+ *							level that is itself partitioned)
+ */
+typedef struct PartitionTreeNodeData
+{
+	PartitionKeyExecInfo *pkinfo;
+	PartitionDesc		pdesc;
+	Oid					relid;
+	int					index;
+	int					offset;
+	int					num_leaf_parts;
+
+	struct PartitionTreeNodeData *downlink;
+	struct PartitionTreeNodeData *next;
+} PartitionTreeNodeData;
+
 /* Support RelationBuildPartitionKey() */
 static PartitionKey copy_partition_key(PartitionKey fromkey);
 static KeyTypeCollInfo *copy_key_type_coll_info(int nkeycols,
@@ -153,6 +208,10 @@ static Oid get_partition_operator(PartitionKey key, int col, StrategyNumber stra
 /* Support RelationGetPartitionCheckQual() */
 static List *generate_partition_check_qual(Relation rel);
 
+/* Support RelationGetPartitionTreeNode() */
+static PartitionTreeNode GetPartitionTreeNodeRecurse(Relation rel, int offset);
+static int get_leaf_partition_count(PartitionTreeNode ptnode);
+
 /* List partition related support functions */
 static PartitionListInfo *make_list_from_spec(PartitionKey key,
 							PartitionListSpec *list_spec);
@@ -815,6 +874,81 @@ RelationGetPartitionCheckQual(Relation rel)
 	return generate_partition_check_qual(rel);
 }
 
+/*
+ * RelationGetPartitionTreeNode
+ *		Recursively form partition tree rooted at this rel's node
+ */
+PartitionTreeNode
+RelationGetPartitionTreeNode(Relation rel)
+{
+	PartitionTreeNode	root;
+
+	/*
+	 * We recurse to build the PartitionTreeNodes for any partitions in the
+	 * partition hierarchy that are themselves partitioned.
+	 */
+	root = GetPartitionTreeNodeRecurse(rel, 0);
+	root->index = 0;
+	root->num_leaf_parts = get_leaf_partition_count(root);
+
+	return root;
+}
+
+/*
+ * get_leaf_partition_oids
+ *		Returns a list of all leaf-level partitions of relation with OID
+ *		'relid'.
+ */
+List *
+get_leaf_partition_oids(Oid relid, int lockmode)
+{
+	List	   *partitions,
+			   *result = NIL;
+	ListCell   *lc;
+
+	partitions = get_partitions(relid, lockmode);
+
+	foreach(lc, partitions)
+	{
+		Oid		myoid = lfirst_oid(lc);
+
+		if (relid_is_partitioned(myoid))
+			result = list_concat(result,
+								 get_leaf_partition_oids(myoid, lockmode));
+
+		result = lappend_oid(result, myoid);
+	}
+
+	return result;
+}
+
+/*
+ * get_leaf_partition_oids_v2
+ * 		Recursively compute the list of OIDs of leaf partitions in the
+ *		partition tree rooted at ptnode
+ */
+List *
+get_leaf_partition_oids_v2(PartitionTreeNode ptnode)
+{
+	int		i;
+	List   *result = NIL;
+	PartitionTreeNode node = ptnode->downlink;
+
+	for (i = 0; i < ptnode->pdesc->nparts; i++)
+	{
+		/* Indexes 0..(node->index - 1) are leaf partitions */
+		if (node && i == node->index)
+		{
+			result = list_concat(result, get_leaf_partition_oids_v2(node));
+			node = node->next;
+		}
+		else
+			result = lappend_oid(result, ptnode->pdesc->parts[i]->oid);
+	}
+
+	return result;
+}
+
 /* Module-local functions */
 
 /*
@@ -1556,6 +1690,103 @@ generate_partition_check_qual(Relation rel)
 	return result;
 }
 
+/*
+ * GetPartitionTreeNodeRecurse
+ *		Workhorse of RelationGetPartitionTreeNode
+ *
+ * 'offset' is 0-based index of the first leaf node in this subtree. During
+ * the first invocation, a 0 will be pass
+ */
+static PartitionTreeNode
+GetPartitionTreeNodeRecurse(Relation rel, int offset)
+{
+	PartitionTreeNode	parent,
+						prev;
+	int					i;
+
+	/* First build our own node */
+	parent = (PartitionTreeNode) palloc0(sizeof(PartitionTreeNodeData));
+	parent->pkinfo = NULL;
+	parent->pdesc = RelationGetPartitionDesc(rel);
+	parent->relid = RelationGetRelid(rel);
+	parent->offset = offset;
+	parent->downlink = NULL;
+	parent->next = NULL;
+
+	/*
+	 * Go through rel's partitions and recursively add nodes for partitions
+	 * that are themselves partitioned.  Link parent to the first child node
+	 * using 'downlink'.  Each new child node is linked to its right sibling
+	 * using 'next'.  Offset value passed when creating a child node is
+	 * determined by looking at the left node if one exists or the parent
+	 * node if it is the first child node of this level.
+	 */
+	prev = NULL;
+	for (i = 0; i < parent->pdesc->nparts; i++)
+	{
+		Oid			relid = parent->pdesc->parts[i]->oid;
+		int			offset;
+		Relation	rel;
+		PartitionTreeNode child;
+
+		/* Skip a leaf partition */
+		if (!relid_is_partitioned(relid))
+			continue;
+
+		rel = heap_open(relid, AccessShareLock);
+
+		if (prev)
+			offset = prev->offset + prev->num_leaf_parts +
+												(i - prev->index - 1);
+		else
+			offset = parent->offset + i;
+
+		child = GetPartitionTreeNodeRecurse(rel, offset);
+		child->index = i;
+		child->num_leaf_parts = get_leaf_partition_count(child);
+
+		heap_close(rel, AccessShareLock);
+
+		/* Found our first child; link to it. */
+		if (parent->downlink == NULL)
+			parent->downlink = child;
+
+		/* Link new node to the left sibling, if any  */
+		if (prev)
+			prev->next = child;
+		prev = child;
+	}
+
+	return parent;
+}
+
+/*
+ * get_leaf_partition_count
+ * 		Recursively count the number of leaf partitions in the partition
+ *		tree rooted at ptnode
+ */
+static int
+get_leaf_partition_count(PartitionTreeNode ptnode)
+{
+	int		i;
+	int 	result = 0;
+	PartitionTreeNode node = ptnode->downlink;
+
+	for (i = 0; i < ptnode->pdesc->nparts; i++)
+	{
+		/* Indexes 0..(node->index - 1) are of leaf partitions */
+		if (node && i == node->index)
+		{
+			result += get_leaf_partition_count(node);
+			node = node->next;
+		}
+		else
+			result += 1;
+	}
+
+	return result;
+}
+
 /* List partition related support functions */
 
 /*
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index db59c9a..89c22dc 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -30,6 +30,7 @@ typedef struct PartitionDescData
 } PartitionDescData;
 
 typedef struct PartitionDescData *PartitionDesc;
+typedef struct PartitionTreeNodeData *PartitionTreeNode;
 
 /* relcache support for partition key information */
 extern void RelationBuildPartitionKey(Relation relation);
@@ -56,4 +57,9 @@ extern Oid get_partition_parent(Oid relid);
 extern List *get_check_qual_from_partbound(Relation rel, Relation parent,
 										   Node *bound);
 extern List *RelationGetPartitionCheckQual(Relation rel);
+
+/* For tuple routing */
+extern PartitionTreeNode RelationGetPartitionTreeNode(Relation rel);
+extern List *get_leaf_partition_oids(Oid relid, int lockmode);
+extern List *get_leaf_partition_oids_v2(PartitionTreeNode ptnode);
 #endif   /* PARTITION_H */
-- 
1.7.1

