From 8966c8d2665e5a7955212472e5e0d7aa7fa48259 Mon Sep 17 00:00:00 2001
From: Amit <amitlangote09@gmail.com>
Date: Sat, 14 Apr 2018 00:22:55 +0900
Subject: [PATCH v4 2/2] Reorganize partitioning cache info

---
 src/backend/catalog/heap.c             |   2 +-
 src/backend/catalog/partition.c        |  53 +++----
 src/backend/catalog/pg_constraint.c    |   8 +-
 src/backend/commands/indexcmds.c       |   8 +-
 src/backend/commands/tablecmds.c       |  75 +++++-----
 src/backend/commands/trigger.c         |  22 +--
 src/backend/executor/execMain.c        |   1 -
 src/backend/executor/execPartition.c   |  84 ++++++-----
 src/backend/optimizer/path/joinrels.c  |   2 +-
 src/backend/optimizer/prep/prepunion.c |  13 +-
 src/backend/optimizer/util/plancat.c   |  77 ++++------
 src/backend/optimizer/util/relnode.c   |   2 +-
 src/backend/parser/parse_utilcmd.c     |   4 +-
 src/backend/partitioning/partbounds.c  |  49 ++++---
 src/backend/partitioning/partprune.c   |  33 +++--
 src/backend/utils/adt/ruleutils.c      |   1 -
 src/backend/utils/cache/partcache.c    | 252 ++++++++++++++++++++++++++++-----
 src/backend/utils/cache/relcache.c     | 134 +++++-------------
 src/include/catalog/partition.h        |  15 --
 src/include/commands/tablecmds.h       |   2 +-
 src/include/executor/execPartition.h   |  12 +-
 src/include/partitioning/partbounds.h  |   1 -
 src/include/partitioning/partprune.h   |   4 +-
 src/include/utils/partcache.h          |  17 ++-
 src/include/utils/rel.h                |  33 +++--
 25 files changed, 520 insertions(+), 384 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 39813de991..bdb47001c8 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3473,7 +3473,7 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
 	 * relcache entry for that partition every time a partition is added or
 	 * removed.
 	 */
-	defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(parent));
+	defaultPartOid = RelationGetDefaultPartitionOid(parent);
 	if (OidIsValid(defaultPartOid))
 		CacheInvalidateRelcacheByRelid(defaultPartOid);
 
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index ad26adaf2f..e7e18d37da 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -274,28 +274,11 @@ has_partition_attrs(Relation rel, Bitmapset *attnums,
 	return false;
 }
 
-/*
- * get_default_oid_from_partdesc
- *
- * Given a partition descriptor, return the OID of the default partition, if
- * one exists; else, return InvalidOid.
- */
-Oid
-get_default_oid_from_partdesc(PartitionDesc partdesc)
-{
-	if (partdesc && partdesc->boundinfo &&
-		partition_bound_has_default(partdesc->boundinfo))
-		return partdesc->oids[partdesc->boundinfo->default_index];
-
-	return InvalidOid;
-}
-
 /*
  * get_default_partition_oid
  *
  * Given a relation OID, return the OID of the default partition, if one
- * exists.  Use get_default_oid_from_partdesc where possible, for
- * efficiency.
+ * exists.  Use get_default_partition_oid where possible, for efficiency.
  */
 Oid
 get_default_partition_oid(Oid parentId)
@@ -475,23 +458,39 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 	if (my_extra == NULL || my_extra->relid != parentId)
 	{
 		Relation	parent;
-		PartitionKey key;
+		PartitionKey	key;
+		FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
 		int			j;
 
 		/* Open parent relation and fetch partition keyinfo */
 		parent = try_relation_open(parentId, AccessShareLock);
 		if (parent == NULL)
 			PG_RETURN_NULL();
-		key = RelationGetPartitionKey(parent);
 
 		/* Reject parent table that is not hash-partitioned. */
-		if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE ||
-			key->strategy != PARTITION_STRATEGY_HASH)
+		if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 					 errmsg("\"%s\" is not a hash partitioned table",
 							get_rel_name(parentId))));
 
+		key = RelationGetPartitionKey(parent);
+		Assert(key != NULL);
+
+		if (key->strategy != PARTITION_STRATEGY_HASH)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("\"%s\" is not a hash partitioned table",
+							get_rel_name(parentId))));
+
+		/* Get partsupfunc FmgrInfo's. */
+		for (j = 0; j < key->partnatts; j++)
+		{
+			fmgr_info_copy(&partsupfunc[j],
+						   partition_getprocinfo(parent, key, j),
+						   CurrentMemoryContext);
+		}
+
 		if (!get_fn_expr_variadic(fcinfo->flinfo))
 		{
 			int			nargs = PG_NARGS() - 3;
@@ -506,7 +505,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 			/* allocate space for our cache */
 			fcinfo->flinfo->fn_extra =
 				MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
-									   offsetof(ColumnsHashData, partsupfunc) +
+									   offsetof(ColumnsHashData,
+												partsupfunc) +
 									   sizeof(FmgrInfo) * nargs);
 			my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
 			my_extra->relid = parentId;
@@ -524,7 +524,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 									j + 1, format_type_be(key->parttypid[j]), format_type_be(argtype))));
 
 				fmgr_info_copy(&my_extra->partsupfunc[j],
-							   &key->partsupfunc[j],
+							   &partsupfunc[j],
 							   fcinfo->flinfo->fn_mcxt);
 			}
 
@@ -536,7 +536,8 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 			/* allocate space for our cache -- just one FmgrInfo in this case */
 			fcinfo->flinfo->fn_extra =
 				MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
-									   offsetof(ColumnsHashData, partsupfunc) +
+									   offsetof(ColumnsHashData,
+												partsupfunc) +
 									   sizeof(FmgrInfo));
 			my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
 			my_extra->relid = parentId;
@@ -558,7 +559,7 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
 									format_type_be(my_extra->variadic_type))));
 
 			fmgr_info_copy(&my_extra->partsupfunc[0],
-						   &key->partsupfunc[0],
+						   &partsupfunc[0],
 						   fcinfo->flinfo->fn_mcxt);
 		}
 
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 8ba9890ca6..32386b246f 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -623,12 +623,14 @@ CloneForeignKeyConstraints(Oid parentId, Oid relationId, List **cloned)
 
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
-		PartitionDesc	partdesc = RelationGetPartitionDesc(rel);
+		int			nparts = RelationGetPartitionCount(rel);
+		Oid		   *partoids = RelationGetPartitionOids(rel);
 		int			i;
 
-		for (i = 0; i < partdesc->nparts; i++)
+		Assert(partoids != NULL || nparts == 0);
+		for (i = 0; i < nparts; i++)
 			CloneForeignKeyConstraints(RelationGetRelid(rel),
-									   partdesc->oids[i],
+									   partoids[i],
 									   cloned);
 	}
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 78302544db..913faddb55 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -877,15 +877,12 @@ DefineIndex(Oid relationId,
 		 */
 		if (!stmt->relation || stmt->relation->inh)
 		{
-			PartitionDesc partdesc = RelationGetPartitionDesc(rel);
-			int			nparts = partdesc->nparts;
-			Oid		   *part_oids = palloc(sizeof(Oid) * nparts);
+			int			nparts = RelationGetPartitionCount(rel);
+			Oid		   *part_oids = RelationGetPartitionOids(rel);
 			bool		invalidate_parent = false;
 			TupleDesc	parentDesc;
 			Oid		   *opfamOids;
 
-			memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
-
 			parentDesc = CreateTupleDescCopy(RelationGetDescr(rel));
 			opfamOids = palloc(sizeof(Oid) * numberOfKeyAttributes);
 			for (i = 0; i < numberOfKeyAttributes; i++)
@@ -901,6 +898,7 @@ DefineIndex(Oid relationId,
 			 * If none matches, build a new index by calling ourselves
 			 * recursively with the same options (except for the index name).
 			 */
+			Assert(part_oids != NULL || nparts == 0);
 			for (i = 0; i < nparts; i++)
 			{
 				Oid		childRelid = part_oids[i];
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c1a9bda433..fe4265d4bb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -829,8 +829,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
 		 * the places such that lock parent, lock default partition and then
 		 * lock the partition so as to avoid a deadlock.
 		 */
-		defaultPartOid =
-			get_default_oid_from_partdesc(RelationGetPartitionDesc(parent));
+		defaultPartOid = RelationGetDefaultPartitionOid(parent);
 		if (OidIsValid(defaultPartOid))
 			defaultRel = heap_open(defaultPartOid, AccessExclusiveLock);
 
@@ -5864,18 +5863,14 @@ ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
 	 * If the parent is a partitioned table, like check constraints, we do not
 	 * support removing the NOT NULL while partitions exist.
 	 */
-	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-	{
-		PartitionDesc partdesc = RelationGetPartitionDesc(rel);
-
-		Assert(partdesc != NULL);
-		if (partdesc->nparts > 0 && !recurse && !recursing)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-					 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
-					 errhint("Do not specify the ONLY keyword.")));
-	}
+	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		RelationGetPartitionCount(rel) > 0 && !recurse && !recursing)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
+				 errhint("Do not specify the ONLY keyword.")));
 }
+
 static ObjectAddress
 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
 {
@@ -6009,16 +6004,12 @@ ATPrepSetNotNull(Relation rel, bool recurse, bool recursing)
 	 * constraints must be added to the child tables.  Complain if requested
 	 * otherwise and partitions exist.
 	 */
-	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-	{
-		PartitionDesc partdesc = RelationGetPartitionDesc(rel);
-
-		if (partdesc && partdesc->nparts > 0 && !recurse && !recursing)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-					 errmsg("cannot add constraint to only the partitioned table when partitions exist"),
-					 errhint("Do not specify the ONLY keyword.")));
-	}
+	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+		RelationGetPartitionCount(rel) > 0 && !recurse && !recursing)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot add constraint to only the partitioned table when partitions exist"),
+				 errhint("Do not specify the ONLY keyword.")));
 }
 
 /*
@@ -7699,13 +7690,13 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	 */
 	if (recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
-		PartitionDesc partdesc;
+		int			nparts = RelationGetPartitionCount(rel);
+		Oid		   *partoids = RelationGetPartitionOids(rel);
 
-		partdesc = RelationGetPartitionDesc(rel);
-
-		for (i = 0; i < partdesc->nparts; i++)
+		Assert(partoids != NULL || nparts == 0);
+		for (i = 0; i < nparts; i++)
 		{
-			Oid			partitionId = partdesc->oids[i];
+			Oid			partitionId = partoids[i];
 			Relation	partition = heap_open(partitionId, lockmode);
 			AlteredTableInfo *childtab;
 			ObjectAddress childAddr;
@@ -13989,10 +13980,12 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
 	}
 	else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
-		PartitionDesc partdesc = RelationGetPartitionDesc(scanrel);
+		int			nparts = RelationGetPartitionCount(scanrel);
+		Oid		   *partoids = RelationGetPartitionOids(scanrel);
 		int			i;
 
-		for (i = 0; i < partdesc->nparts; i++)
+		Assert(partoids != NULL || nparts == 0);
+		for (i = 0; i < nparts; i++)
 		{
 			Relation	part_rel;
 			bool		found_whole_row;
@@ -14001,7 +13994,7 @@ QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
 			/*
 			 * This is the minimum lock we need to prevent deadlocks.
 			 */
-			part_rel = heap_open(partdesc->oids[i], AccessExclusiveLock);
+			part_rel = heap_open(partoids[i], AccessExclusiveLock);
 
 			/*
 			 * Adjust the constraint for scanrel so that it matches this
@@ -14051,8 +14044,7 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
 	 * We must lock the default partition if one exists, because attaching a
 	 * new partition will change its partition constraint.
 	 */
-	defaultPartOid =
-		get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
+	defaultPartOid = RelationGetDefaultPartitionOid(rel);
 	if (OidIsValid(defaultPartOid))
 		LockRelationOid(defaultPartOid, AccessExclusiveLock);
 
@@ -14628,8 +14620,7 @@ ATExecDetachPartition(Relation rel, RangeVar *name)
 	 * We must lock the default partition, because detaching this partition
 	 * will change its partition constraint.
 	 */
-	defaultPartOid =
-		get_default_oid_from_partdesc(RelationGetPartitionDesc(rel));
+	defaultPartOid = RelationGetDefaultPartitionOid(rel);
 	if (OidIsValid(defaultPartOid))
 		LockRelationOid(defaultPartOid, AccessExclusiveLock);
 
@@ -14830,8 +14821,9 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 		AttrNumber *attmap;
 		bool		found;
 		int			i;
-		PartitionDesc partDesc;
-		Oid			constraintOid,
+		int			nparts = RelationGetPartitionCount(parentTbl);
+		Oid		   *partoids = RelationGetPartitionOids(parentTbl),
+					constraintOid,
 					cldConstrId = InvalidOid;
 
 		/*
@@ -14849,11 +14841,11 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
 							   RelationGetRelationName(partIdx))));
 
 		/* Make sure it indexes a partition of the other index's table */
-		partDesc = RelationGetPartitionDesc(parentTbl);
 		found = false;
-		for (i = 0; i < partDesc->nparts; i++)
+		Assert(partoids != NULL || nparts == 0);
+		for (i = 0; i < nparts; i++)
 		{
-			if (partDesc->oids[i] == state.partitionOid)
+			if (partoids[i] == state.partitionOid)
 			{
 				found = true;
 				break;
@@ -14984,6 +14976,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
 	int				tuples = 0;
 	HeapTuple		inhTup;
 	bool			updated = false;
+	int				nparts = RelationGetPartitionCount(partedTbl);
 
 	Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
 
@@ -15023,7 +15016,7 @@ validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
 	 * If we found as many inherited indexes as the partitioned table has
 	 * partitions, we're good; update pg_index to set indisvalid.
 	 */
-	if (tuples == RelationGetPartitionDesc(partedTbl)->nparts)
+	if (tuples == nparts)
 	{
 		Relation	idxRel;
 		HeapTuple	newtup;
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index cc50691aa0..d4fdf1b556 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1086,7 +1086,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	 */
 	if (partition_recurse)
 	{
-		PartitionDesc partdesc = RelationGetPartitionDesc(rel);
+		int			nparts = RelationGetPartitionCount(rel);
+		Oid		   *partoids = RelationGetPartitionOids(rel);
 		List	   *idxs = NIL;
 		List	   *childTbls = NIL;
 		ListCell   *l;
@@ -1118,7 +1119,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 		oldcxt = MemoryContextSwitchTo(perChildCxt);
 
 		/* Iterate to create the trigger on each existing partition */
-		for (i = 0; i < partdesc->nparts; i++)
+		Assert(partoids != NULL || nparts == 0);
+		for (i = 0; i < nparts; i++)
 		{
 			Oid			indexOnChild = InvalidOid;
 			ListCell   *l2;
@@ -1127,14 +1129,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 			Node	   *qual;
 			bool		found_whole_row;
 
-			childTbl = heap_open(partdesc->oids[i], ShareRowExclusiveLock);
+			childTbl = heap_open(partoids[i], ShareRowExclusiveLock);
 
 			/* Find which of the child indexes is the one on this partition */
 			if (OidIsValid(indexOid))
 			{
 				forboth(l, idxs, l2, childTbls)
 				{
-					if (lfirst_oid(l2) == partdesc->oids[i])
+					if (lfirst_oid(l2) == partoids[i])
 					{
 						indexOnChild = lfirst_oid(l);
 						break;
@@ -1143,7 +1145,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 				if (!OidIsValid(indexOnChild))
 					elog(ERROR, "failed to find index matching index \"%s\" in partition \"%s\"",
 						 get_rel_name(indexOid),
-						 get_rel_name(partdesc->oids[i]));
+						 get_rel_name(partoids[i]));
 			}
 
 			/*
@@ -1171,7 +1173,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 				elog(ERROR, "unexpected whole-row reference found in trigger WHEN clause");
 
 			CreateTrigger(childStmt, queryString,
-						  partdesc->oids[i], refRelOid,
+						  partoids[i], refRelOid,
 						  InvalidOid, indexOnChild,
 						  funcoid, trigoid, qual,
 						  isInternal, true);
@@ -1854,14 +1856,16 @@ EnableDisableTrigger(Relation rel, const char *tgname,
 			if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
 				(TRIGGER_FOR_ROW(oldtrig->tgtype)))
 			{
-				PartitionDesc partdesc = RelationGetPartitionDesc(rel);
+				int		nparts = RelationGetPartitionCount(rel);
+				Oid	   *partoids = RelationGetPartitionOids(rel);
 				int			i;
 
-				for (i = 0; i < partdesc->nparts; i++)
+				Assert(partoids != NULL || nparts == 0);
+				for (i = 0; i < nparts; i++)
 				{
 					Relation	part;
 
-					part = relation_open(partdesc->oids[i], lockmode);
+					part = relation_open(partoids[i], lockmode);
 					EnableDisableTrigger(part, NameStr(oldtrig->tgname),
 										 fires_when, skip_system, lockmode);
 					heap_close(part, NoLock);	/* keep lock till commit */
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4fa713bbe5..e885a16647 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -42,7 +42,6 @@
 #include "access/transam.h"
 #include "access/xact.h"
 #include "catalog/namespace.h"
-#include "catalog/partition.h"
 #include "catalog/pg_publication.h"
 #include "commands/matview.h"
 #include "commands/trigger.h"
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 1bf4412be5..e7652cbd25 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -22,6 +22,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/rls.h"
 #include "utils/ruleutils.h"
@@ -35,7 +36,7 @@ static void FormPartitionKeyDatum(PartitionDispatch pd,
 					  EState *estate,
 					  Datum *values,
 					  bool *isnull);
-static int get_partition_for_tuple(Relation relation, Datum *values,
+static int get_partition_for_tuple(PartitionDispatch pd, Datum *values,
 					  bool *isnull);
 static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
 									 Datum *values,
@@ -203,13 +204,11 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 	parent = pd[0];
 	while (true)
 	{
-		PartitionDesc partdesc;
 		TupleTableSlot *myslot = parent->tupslot;
 		TupleConversionMap *map = parent->tupmap;
 		int			cur_index = -1;
 
 		rel = parent->reldesc;
-		partdesc = RelationGetPartitionDesc(rel);
 
 		/*
 		 * Convert the tuple to this parent's layout so that we can do certain
@@ -240,13 +239,13 @@ ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd,
 		 * Nothing for get_partition_for_tuple() to do if there are no
 		 * partitions to begin with.
 		 */
-		if (partdesc->nparts == 0)
+		if (parent->nparts == 0)
 		{
 			result = -1;
 			break;
 		}
 
-		cur_index = get_partition_for_tuple(rel, values, isnull);
+		cur_index = get_partition_for_tuple(parent, values, isnull);
 
 		/*
 		 * cur_index < 0 means we failed to find a partition of this parent.
@@ -876,8 +875,10 @@ get_partition_dispatch_recurse(Relation rel, Relation parent,
 							   List **pds, List **leaf_part_oids)
 {
 	TupleDesc	tupdesc = RelationGetDescr(rel);
-	PartitionDesc partdesc = RelationGetPartitionDesc(rel);
 	PartitionKey partkey = RelationGetPartitionKey(rel);
+	int			nparts = RelationGetPartitionCount(rel);
+	Oid		   *partoids = RelationGetPartitionOids(rel);
+	PartitionBoundInfo boundinfo = RelationGetPartitionBounds(rel);
 	PartitionDispatch pd;
 	int			i;
 
@@ -889,7 +890,15 @@ get_partition_dispatch_recurse(Relation rel, Relation parent,
 	pd->reldesc = rel;
 	pd->key = partkey;
 	pd->keystate = NIL;
-	pd->partdesc = partdesc;
+	/* Get partsupfunc FmgrInfo's. */
+	for (i = 0; i < partkey->partnatts; i++)
+	{
+		fmgr_info_copy(&pd->partsupfunc[i],
+					   partition_getprocinfo(rel, partkey, i),
+					   CurrentMemoryContext);
+	}
+	pd->nparts = nparts;
+	pd->boundinfo = boundinfo;
 	if (parent != NULL)
 	{
 		/*
@@ -934,10 +943,10 @@ get_partition_dispatch_recurse(Relation rel, Relation parent,
 	 * the tree.  This value is used to continue the search in the next level
 	 * of the partition tree.
 	 */
-	pd->indexes = (int *) palloc(partdesc->nparts * sizeof(int));
-	for (i = 0; i < partdesc->nparts; i++)
+	pd->indexes = (int *) palloc(nparts * sizeof(int));
+	for (i = 0; i < nparts; i++)
 	{
-		Oid			partrelid = partdesc->oids[i];
+		Oid			partrelid = partoids[i];
 
 		if (get_rel_relkind(partrelid) != RELKIND_PARTITIONED_TABLE)
 		{
@@ -1029,26 +1038,26 @@ FormPartitionKeyDatum(PartitionDispatch pd,
  *		Finds partition of relation which accepts the partition key specified
  *		in values and isnull
  *
- * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
- * found or -1 if none found.
+ * Return value is index of the partition (>= 0 and < nparts) if one found or
+ * -1 if none found.
  */
-int
-get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
+static int
+get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 {
 	int			bound_offset;
 	int			part_index = -1;
-	PartitionKey key = RelationGetPartitionKey(relation);
-	PartitionDesc partdesc = RelationGetPartitionDesc(relation);
+	PartitionKey key = pd->key;
+	FmgrInfo   *partsupfunc = pd->partsupfunc;
+	PartitionBoundInfo boundinfo = pd->boundinfo;
 
 	/* Route as appropriate based on partitioning strategy. */
 	switch (key->strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
 			{
-				PartitionBoundInfo boundinfo = partdesc->boundinfo;
 				int			greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
 				uint64		rowHash = compute_hash_value(key->partnatts,
-														 key->partsupfunc,
+														 partsupfunc,
 														 values, isnull);
 
 				part_index = boundinfo->indexes[rowHash % greatest_modulus];
@@ -1058,19 +1067,19 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
 		case PARTITION_STRATEGY_LIST:
 			if (isnull[0])
 			{
-				if (partition_bound_accepts_nulls(partdesc->boundinfo))
-					part_index = partdesc->boundinfo->null_index;
+				if (partition_bound_accepts_nulls(boundinfo))
+					part_index = boundinfo->null_index;
 			}
 			else
 			{
 				bool		equal = false;
 
-				bound_offset = partition_list_bsearch(key->partsupfunc,
+				bound_offset = partition_list_bsearch(partsupfunc,
 													  key->partcollation,
-													  partdesc->boundinfo,
+													  boundinfo,
 													  values[0], &equal);
 				if (bound_offset >= 0 && equal)
-					part_index = partdesc->boundinfo->indexes[bound_offset];
+					part_index = boundinfo->indexes[bound_offset];
 			}
 			break;
 
@@ -1095,9 +1104,9 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
 
 				if (!range_partkey_has_null)
 				{
-					bound_offset = partition_range_datum_bsearch(key->partsupfunc,
+					bound_offset = partition_range_datum_bsearch(partsupfunc,
 																 key->partcollation,
-																 partdesc->boundinfo,
+																 boundinfo,
 																 key->partnatts,
 																 values,
 																 &equal);
@@ -1108,7 +1117,7 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
 					 * bound of the partition we're looking for, if there
 					 * actually exists one.
 					 */
-					part_index = partdesc->boundinfo->indexes[bound_offset + 1];
+					part_index = boundinfo->indexes[bound_offset + 1];
 				}
 			}
 			break;
@@ -1123,7 +1132,7 @@ get_partition_for_tuple(Relation relation, Datum *values, bool *isnull)
 	 * the default partition, if there is one.
 	 */
 	if (part_index < 0)
-		part_index = partdesc->boundinfo->default_index;
+		part_index = boundinfo->default_index;
 
 	return part_index;
 }
@@ -1142,7 +1151,7 @@ ExecBuildSlotPartitionKeyDescription(Relation rel,
 									 int maxfieldlen)
 {
 	StringInfoData buf;
-	PartitionKey key = RelationGetPartitionKey(rel);
+	PartitionKey	key = RelationGetPartitionKey(rel);
 	int			partnatts = get_partition_natts(key);
 	int			i;
 	Oid			relid = RelationGetRelid(rel);
@@ -1390,10 +1399,12 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
 		PartitionPruneInfo *pinfo = (PartitionPruneInfo *) lfirst(lc);
 		PartitionPruningData *pprune = &prunedata[i];
 		PartitionPruneContext *context = &pprune->context;
-		PartitionDesc partdesc;
 		Relation	rel;
 		PartitionKey partkey;
+		int			nparts;
+		PartitionBoundInfo boundinfo;
 		int			partnatts;
+		int			j;
 
 		pprune->present_parts = bms_copy(pinfo->present_parts);
 		pprune->subnode_map = palloc(sizeof(int) * pinfo->nparts);
@@ -1415,16 +1426,23 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
 		rel = relation_open(pinfo->reloid, NoLock);
 
 		partkey = RelationGetPartitionKey(rel);
-		partdesc = RelationGetPartitionDesc(rel);
+		nparts = RelationGetPartitionCount(rel);
+		boundinfo = RelationGetPartitionBounds(rel);
 
 		context->strategy = partkey->strategy;
 		context->partnatts = partnatts = partkey->partnatts;
 		context->partopfamily = partkey->partopfamily;
 		context->partopcintype = partkey->partopcintype;
 		context->partcollation = partkey->partcollation;
-		context->partsupfunc = partkey->partsupfunc;
-		context->nparts = pinfo->nparts;
-		context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
+		/* Get partsupfunc FmgrInfo's. */
+		for (j = 0; j < partkey->partnatts; j++)
+		{
+			fmgr_info_copy(&context->partsupfunc[j],
+						   partition_getprocinfo(rel, partkey, j),
+						   CurrentMemoryContext);
+		}
+		context->nparts = nparts;
+		context->boundinfo = boundinfo;
 		context->planstate = planstate;
 		context->safeparams = NULL; /* empty for now */
 
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 2e289d475e..5d66cfb87b 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -15,12 +15,12 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
-#include "catalog/partition.h"
 #include "optimizer/clauses.h"
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/prep.h"
+#include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 8d86e98adc..23ce22a27e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1584,7 +1584,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	oldrelation = heap_open(parentOID, NoLock);
 
 	/* Scan the inheritance set and expand it */
-	if (RelationGetPartitionDesc(oldrelation) != NULL)
+	if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
 		Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
 
@@ -1675,13 +1675,11 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 	RangeTblEntry *childrte;
 	Index		childRTindex;
 	bool		has_child = false;
-	PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
+	int			nparts = RelationGetPartitionCount(parentrel);
+	Oid		   *partoids = RelationGetPartitionOids(parentrel);
 
 	check_stack_depth();
 
-	/* A partitioned table should always have a partition descriptor. */
-	Assert(partdesc);
-
 	Assert(parentrte->inh);
 
 	/*
@@ -1700,9 +1698,10 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 									top_parentrc, parentrel,
 									appinfos, &childrte, &childRTindex);
 
-	for (i = 0; i < partdesc->nparts; i++)
+	Assert(partoids != NULL || nparts == 0);
+	for (i = 0; i < nparts; i++)
 	{
-		Oid			childOID = partdesc->oids[i];
+		Oid			childOID = partoids[i];
 		Relation	childrel;
 
 		/* Open rel; we already have required locks */
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index cb9cfc3729..23fed6a93e 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -27,7 +27,6 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
-#include "catalog/partition.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_statistic_ext.h"
 #include "foreign/fdwapi.h"
@@ -45,8 +44,9 @@
 #include "storage/bufmgr.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
-#include "utils/syscache.h"
+#include "utils/partcache.h"
 #include "utils/rel.h"
+#include "utils/syscache.h"
 #include "utils/snapmgr.h"
 
 
@@ -70,8 +70,9 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
 static List *get_relation_statistics(RelOptInfo *rel, Relation relation);
 static void set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
 							Relation relation);
-static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel);
-static void set_baserel_partition_key_exprs(Relation relation,
+static PartitionScheme find_partition_scheme(PlannerInfo *root, Relation rel,
+							PartitionKey partkey);
+static void set_baserel_partition_key_exprs(PartitionKey partkey,
 								RelOptInfo *rel);
 
 /*
@@ -1869,18 +1870,19 @@ static void
 set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
 							Relation relation)
 {
-	PartitionDesc partdesc;
-	PartitionKey partkey;
+	PartitionKey partkey = RelationGetPartitionKey(relation);
+	int		nparts = RelationGetPartitionCount(relation);
+	PartitionBoundInfo boundinfo = RelationGetPartitionBounds(relation);
 
 	Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	Assert(partkey != NULL);
+	Assert(boundinfo != NULL || nparts == 0);
 
-	partdesc = RelationGetPartitionDesc(relation);
-	partkey = RelationGetPartitionKey(relation);
-	rel->part_scheme = find_partition_scheme(root, relation);
-	Assert(partdesc != NULL && rel->part_scheme != NULL);
-	rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
-	rel->nparts = partdesc->nparts;
-	set_baserel_partition_key_exprs(relation, rel);
+	rel->part_scheme = find_partition_scheme(root, relation, partkey);
+	Assert(rel->part_scheme != NULL);
+	rel->boundinfo = boundinfo;
+	rel->nparts = nparts;
+	set_baserel_partition_key_exprs(partkey, rel);
 	rel->partition_qual = RelationGetPartitionQual(relation);
 }
 
@@ -1890,9 +1892,9 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
  * Find or create a PartitionScheme for this Relation.
  */
 static PartitionScheme
-find_partition_scheme(PlannerInfo *root, Relation relation)
+find_partition_scheme(PlannerInfo *root, Relation rel,
+					  PartitionKey partkey)
 {
-	PartitionKey partkey = RelationGetPartitionKey(relation);
 	ListCell   *lc;
 	int			partnatts,
 				i;
@@ -1933,14 +1935,11 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
 
 		/*
 		 * If partopfamily and partopcintype matched, must have the same
-		 * partition comparison functions.  Note that we cannot reliably
-		 * Assert the equality of function structs themselves for they might
-		 * be different across PartitionKey's, so just Assert for the function
-		 * OIDs.
+		 * partition comparison functions.
 		 */
 #ifdef USE_ASSERT_CHECKING
 		for (i = 0; i < partkey->partnatts; i++)
-			Assert(partkey->partsupfunc[i].fn_oid ==
+			Assert(partkey->partsupfuncid[i] ==
 				   part_scheme->partsupfunc[i].fn_oid);
 #endif
 
@@ -1949,39 +1948,22 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
 	}
 
 	/*
-	 * Did not find matching partition scheme. Create one copying relevant
-	 * information from the relcache. We need to copy the contents of the
-	 * array since the relcache entry may not survive after we have closed the
-	 * relation.
+	 * Did not find matching partition scheme. Create one usinng the
+	 * relevant information copied from the relcache.
 	 */
 	part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData));
 	part_scheme->strategy = partkey->strategy;
 	part_scheme->partnatts = partkey->partnatts;
-
-	part_scheme->partopfamily = (Oid *) palloc(sizeof(Oid) * partnatts);
-	memcpy(part_scheme->partopfamily, partkey->partopfamily,
-		   sizeof(Oid) * partnatts);
-
-	part_scheme->partopcintype = (Oid *) palloc(sizeof(Oid) * partnatts);
-	memcpy(part_scheme->partopcintype, partkey->partopcintype,
-		   sizeof(Oid) * partnatts);
-
-	part_scheme->partcollation = (Oid *) palloc(sizeof(Oid) * partnatts);
-	memcpy(part_scheme->partcollation, partkey->partcollation,
-		   sizeof(Oid) * partnatts);
-
-	part_scheme->parttyplen = (int16 *) palloc(sizeof(int16) * partnatts);
-	memcpy(part_scheme->parttyplen, partkey->parttyplen,
-		   sizeof(int16) * partnatts);
-
-	part_scheme->parttypbyval = (bool *) palloc(sizeof(bool) * partnatts);
-	memcpy(part_scheme->parttypbyval, partkey->parttypbyval,
-		   sizeof(bool) * partnatts);
-
+	part_scheme->partopfamily = partkey->partopfamily;
+	part_scheme->partopcintype = partkey->partopcintype;
+	part_scheme->partcollation = partkey->partcollation;
+	part_scheme->parttyplen = partkey->parttyplen;
+	part_scheme->parttypbyval = partkey->parttypbyval;
 	part_scheme->partsupfunc = (FmgrInfo *)
 		palloc(sizeof(FmgrInfo) * partnatts);
 	for (i = 0; i < partnatts; i++)
-		fmgr_info_copy(&part_scheme->partsupfunc[i], &partkey->partsupfunc[i],
+		fmgr_info_copy(&part_scheme->partsupfunc[i],
+					   partition_getprocinfo(rel, partkey, i),
 					   CurrentMemoryContext);
 
 	/* Add the partitioning scheme to PlannerInfo. */
@@ -1998,10 +1980,9 @@ find_partition_scheme(PlannerInfo *root, Relation relation)
  * nodes.  All Var nodes are restamped with the relid of given relation.
  */
 static void
-set_baserel_partition_key_exprs(Relation relation,
+set_baserel_partition_key_exprs(PartitionKey partkey,
 								RelOptInfo *rel)
 {
-	PartitionKey partkey = RelationGetPartitionKey(relation);
 	int			partnatts;
 	int			cnt;
 	List	  **partexprs;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b9aa7486ba..fc19f892c5 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -17,7 +17,6 @@
 #include <limits.h>
 
 #include "miscadmin.h"
-#include "catalog/partition.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
@@ -27,6 +26,7 @@
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
+#include "partitioning/partbounds.h"
 #include "utils/hsearch.h"
 
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 9178139912..7889f8bfab 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3556,7 +3556,7 @@ transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd)
 	{
 		case RELKIND_PARTITIONED_TABLE:
 			/* transform the partition bound, if any */
-			Assert(RelationGetPartitionKey(parentRel) != NULL);
+			Assert(parentRel->rd_partkey != NULL);
 			if (cmd->bound != NULL)
 				cxt->partbound = transformPartitionBound(cxt->pstate, parentRel,
 														 cmd->bound);
@@ -3597,7 +3597,7 @@ transformPartitionBound(ParseState *pstate, Relation parent,
 						PartitionBoundSpec *spec)
 {
 	PartitionBoundSpec *result_spec;
-	PartitionKey key = RelationGetPartitionKey(parent);
+	PartitionKey	key = RelationGetPartitionKey(parent);
 	char		strategy = get_partition_strategy(key);
 	int			partnatts = get_partition_natts(key);
 	List	   *partexprs = get_partition_exprs(key);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 5c4fc90341..b25e74896b 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -311,8 +311,9 @@ check_new_partition_bound(char *relname, Relation parent,
 						  PartitionBoundSpec *spec)
 {
 	PartitionKey	key = RelationGetPartitionKey(parent);
-	PartitionDesc partdesc = RelationGetPartitionDesc(parent);
-	PartitionBoundInfo boundinfo = partdesc->boundinfo;
+	int				nparts = RelationGetPartitionCount(parent);
+	Oid			   *partoids = RelationGetPartitionOids(parent);
+	PartitionBoundInfo boundinfo = RelationGetPartitionBounds(parent);
 	ParseState *pstate = make_parsestate(NULL);
 	int			with = -1;
 	bool		overlap = false;
@@ -332,7 +333,7 @@ check_new_partition_bound(char *relname, Relation parent,
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("partition \"%s\" conflicts with existing default partition \"%s\"",
-						relname, get_rel_name(partdesc->oids[boundinfo->default_index])),
+						relname, get_rel_name(partoids[boundinfo->default_index])),
 				 parser_errposition(pstate, spec->location)));
 	}
 
@@ -343,7 +344,7 @@ check_new_partition_bound(char *relname, Relation parent,
 				Assert(spec->strategy == PARTITION_STRATEGY_HASH);
 				Assert(spec->remainder >= 0 && spec->remainder < spec->modulus);
 
-				if (partdesc->nparts > 0)
+				if (nparts > 0)
 				{
 					Datum	  **datums = boundinfo->datums;
 					int			ndatums = boundinfo->ndatums;
@@ -422,9 +423,16 @@ check_new_partition_bound(char *relname, Relation parent,
 
 		case PARTITION_STRATEGY_LIST:
 			{
+				FmgrInfo partsupfunc;
+
 				Assert(spec->strategy == PARTITION_STRATEGY_LIST);
 
-				if (partdesc->nparts > 0)
+				/* Get partsupfunc FmgrInfo for the only key. */
+				fmgr_info_copy(&partsupfunc,
+							   partition_getprocinfo(parent, key, 0),
+							   CurrentMemoryContext);
+
+				if (nparts > 0)
 				{
 					ListCell   *cell;
 
@@ -443,7 +451,7 @@ check_new_partition_bound(char *relname, Relation parent,
 							int			offset;
 							bool		equal;
 
-							offset = partition_list_bsearch(&key->partsupfunc[0],
+							offset = partition_list_bsearch(&partsupfunc,
 														key->partcollation,
 															boundinfo,
 															val->constvalue,
@@ -471,6 +479,16 @@ check_new_partition_bound(char *relname, Relation parent,
 			{
 				PartitionRangeBound *lower,
 						   *upper;
+				FmgrInfo partsupfunc[PARTITION_MAX_KEYS];
+				int		i;
+
+				/* Get partsupfunc FmgrInfo's. */
+				for (i = 0; i < key->partnatts; i++)
+				{
+					fmgr_info_copy(&partsupfunc[i],
+								   partition_getprocinfo(parent, key, i),
+								   CurrentMemoryContext);
+				}
 
 				Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
 				lower = make_one_range_bound(key, -1, spec->lowerdatums, true);
@@ -480,7 +498,7 @@ check_new_partition_bound(char *relname, Relation parent,
 				 * First check if the resulting range would be empty with
 				 * specified lower and upper bounds
 				 */
-				if (partition_rbound_cmp(key->partnatts, key->partsupfunc,
+				if (partition_rbound_cmp(key->partnatts, partsupfunc,
 										 key->partcollation, lower->datums,
 										 lower->kind, true, upper) >= 0)
 				{
@@ -494,7 +512,7 @@ check_new_partition_bound(char *relname, Relation parent,
 							 parser_errposition(pstate, spec->location)));
 				}
 
-				if (partdesc->nparts > 0)
+				if (nparts > 0)
 				{
 					int			offset;
 					bool		equal;
@@ -520,7 +538,7 @@ check_new_partition_bound(char *relname, Relation parent,
 					 * at the end.
 					 */
 					offset = partition_range_bsearch(key->partnatts,
-													 key->partsupfunc,
+													 partsupfunc,
 													 key->partcollation,
 													 boundinfo, lower,
 													 &equal);
@@ -545,7 +563,7 @@ check_new_partition_bound(char *relname, Relation parent,
 							is_lower = (boundinfo->indexes[offset + 1] == -1);
 
 							cmpval = partition_rbound_cmp(key->partnatts,
-														  key->partsupfunc,
+														  partsupfunc,
 														  key->partcollation,
 														  datums, kind,
 														  is_lower, upper);
@@ -586,7 +604,7 @@ check_new_partition_bound(char *relname, Relation parent,
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("partition \"%s\" would overlap partition \"%s\"",
-						relname, get_rel_name(partdesc->oids[with])),
+						relname, get_rel_name(partoids[with])),
 				 parser_errposition(pstate, spec->location)));
 	}
 }
@@ -1434,8 +1452,7 @@ get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
 	{
 		int			i;
 		int			ndatums = 0;
-		PartitionDesc pdesc = RelationGetPartitionDesc(parent);
-		PartitionBoundInfo boundinfo = pdesc->boundinfo;
+		PartitionBoundInfo boundinfo = RelationGetPartitionBounds(parent);
 
 		if (boundinfo)
 		{
@@ -1634,11 +1651,11 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 	if (spec->is_default)
 	{
 		List	   *or_expr_args = NIL;
-		PartitionDesc pdesc = RelationGetPartitionDesc(parent);
-		Oid		   *inhoids = pdesc->oids;
-		int			nparts = pdesc->nparts,
+		Oid		   *inhoids = RelationGetPartitionOids(parent);
+		int			nparts = RelationGetPartitionCount(parent),
 					i;
 
+		Assert(inhoids != NULL || nparts == 0);
 		for (i = 0; i < nparts; i++)
 		{
 			Oid			inhrelid = inhoids[i];
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 7666c6c412..d506bc0264 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -54,7 +54,6 @@
 #include "optimizer/predtest.h"
 #include "optimizer/prep.h"
 #include "partitioning/partprune.h"
-#include "partitioning/partbounds.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
@@ -437,7 +436,10 @@ prune_append_rel_partitions(RelOptInfo *rel)
 	context.partopfamily = rel->part_scheme->partopfamily;
 	context.partopcintype = rel->part_scheme->partopcintype;
 	context.partcollation = rel->part_scheme->partcollation;
-	context.partsupfunc = rel->part_scheme->partsupfunc;
+	for (i = 0; i < context.partnatts; i++)
+		fmgr_info_copy(&context.partsupfunc[i],
+					   &rel->part_scheme->partsupfunc[i],
+					   CurrentMemoryContext);
 	context.nparts = rel->nparts;
 	context.boundinfo = rel->boundinfo;
 
@@ -1413,7 +1415,8 @@ match_clause_to_partition_key(RelOptInfo *rel,
 		partclause->op_is_ne = false;
 		partclause->expr = expr;
 		/* We know that expr is of Boolean type. */
-		partclause->cmpfn = rel->part_scheme->partsupfunc[partkeyidx].fn_oid;
+		partclause->cmpfn =
+						rel->part_scheme->partsupfunc[partkeyidx].fn_oid;
 		partclause->op_strategy = InvalidStrategy;
 
 		*pc = partclause;
@@ -1962,8 +1965,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
  *
  * 'nvalues', the number of Datums in the 'values' array.
  *
- * 'partsupfunc' contains partition hashing functions that can produce correct
- * hash for the type of the values contained in 'values'.
+ * 'partsupfunc' contains partition hashing functions that can produce
+ * correct hash for the type of the values contained in 'values'.
  *
  * 'nullkeys' is the set of partition keys that are null.
  */
@@ -2000,7 +2003,8 @@ get_matching_hash_bounds(PartitionPruneContext *context,
 			isnull[i] = bms_is_member(i, nullkeys);
 
 		greatest_modulus = get_hash_partition_greatest_modulus(boundinfo);
-		rowHash = compute_hash_value(partnatts, partsupfunc, values, isnull);
+		rowHash = compute_hash_value(partnatts, partsupfunc, values,
+									 isnull);
 
 		if (partindices[rowHash % greatest_modulus] >= 0)
 			result->bound_offsets =
@@ -2029,8 +2033,8 @@ get_matching_hash_bounds(PartitionPruneContext *context,
  *
  * 'nvalues', if non-zero, should be exactly 1, because of list partitioning.
  *
- * 'partsupfunc' contains the list partitioning comparison function to be used
- * to perform partition_list_bsearch
+ * 'partsupfunc' contains the list partitioning comparison function to be
+ * used to perform partition_list_bsearch
  *
  * 'nullkeys' is the set of partition keys that are null.
  */
@@ -2102,8 +2106,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 		result->bound_offsets = bms_add_range(NULL, 0,
 											  boundinfo->ndatums - 1);
 
-		off = partition_list_bsearch(partsupfunc, partcollation, boundinfo,
-									 value, &is_equal);
+		off = partition_list_bsearch(partsupfunc, partcollation,
+									 boundinfo, value, &is_equal);
 		if (off >= 0 && is_equal)
 		{
 
@@ -2231,9 +2235,9 @@ get_matching_list_bounds(PartitionPruneContext *context,
  *
  * 'nvalues', number of Datums in 'values' array. Must be <= context->partnatts.
  *
- * 'partsupfunc' contains the range partitioning comparison functions to be
- * used to perform partition_range_datum_bsearch or partition_rbound_datum_cmp
- * using.
+ * 'partsupfunc' contains the range partitioning comparison functions to
+ * be used to perform partition_range_datum_bsearch or
+ * partition_rbound_datum_cmp using.
  *
  * 'nullkeys' is the set of partition keys that are null.
  */
@@ -2797,7 +2801,8 @@ perform_pruning_base_step(PartitionPruneContext *context,
 				cmpfn = lfirst_oid(lc2);
 				Assert(OidIsValid(cmpfn));
 				if (cmpfn != context->partsupfunc[keyno].fn_oid)
-					fmgr_info(cmpfn, &partsupfunc[keyno]);
+					fmgr_info_cxt(cmpfn, &partsupfunc[keyno],
+								  CurrentMemoryContext);
 				else
 					fmgr_info_copy(&partsupfunc[keyno],
 								   &context->partsupfunc[keyno],
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 99643e83b2..ebcfa3b152 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -24,7 +24,6 @@
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
-#include "catalog/partition.h"
 #include "catalog/pg_aggregate.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_authid.h"
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index db91cde40a..c0a8be76bc 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -17,7 +17,6 @@
 #include "access/hash.h"
 #include "access/htup_details.h"
 #include "access/nbtree.h"
-#include "catalog/partition.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
@@ -42,18 +41,37 @@
 #include "utils/partcache.h"
 #include "utils/syscache.h"
 
+/*
+ * Information about partitions of a partitioned table.
+ */
+typedef struct PartitionDescData
+{
+	int			nparts;			/* Number of partitions */
+	Oid		   *oids;			/* OIDs of partitions */
+	PartitionBoundInfo boundinfo;	/* collection of partition bounds */
+} PartitionDescData;
+
+typedef struct PartitionDescData *PartitionDesc;
+
+typedef struct PartitionBoundSortInfo
+{
+	FmgrInfo	   *partsupfunc;
+	PartitionKey	key;
+} PartitionBoundSortInfo;
+
 static int32 qsort_partition_hbound_cmp(const void *a, const void *b);
 static int32 qsort_partition_list_value_cmp(const void *a, const void *b,
 							   void *arg);
 static int32 qsort_partition_rbound_cmp(const void *a, const void *b,
 						   void *arg);
+static PartitionKey partition_key_copy(PartitionKey fromkey);
 static List *generate_partition_qual(Relation rel);
 
 /*
  * RelationBuildPartitionKey
  *		Build and attach to relcache partition key data of relation
  *
- * Partitioning key data is a complex structure; to avoid complicated logic to
+ * Partition key data is of complex structure; to avoid complicated logic to
  * free individual elements whenever the relcache entry is flushed, we give it
  * its own memory context, child of CacheMemoryContext, which can easily be
  * deleted on its own.  To avoid leaking memory in that context in case of an
@@ -77,8 +95,7 @@ RelationBuildPartitionKey(Relation relation)
 	oidvector  *collation;
 	ListCell   *partexprs_item;
 	Datum		datum;
-	MemoryContext partkeycxt,
-				oldcxt;
+	MemoryContext oldcxt;
 	int16		procnum;
 
 	tuple = SearchSysCache1(PARTRELID,
@@ -91,13 +108,7 @@ RelationBuildPartitionKey(Relation relation)
 	if (!HeapTupleIsValid(tuple))
 		return;
 
-	partkeycxt = AllocSetContextCreate(CurTransactionContext,
-									   "partition key",
-									   ALLOCSET_SMALL_SIZES);
-	MemoryContextCopyAndSetIdentifier(partkeycxt,
-								   RelationGetRelationName(relation));
-
-	key = (PartitionKey) MemoryContextAllocZero(partkeycxt,
+	key = (PartitionKey) MemoryContextAllocZero(relation->rd_partcxt,
 												sizeof(PartitionKeyData));
 
 	/* Fixed-length attributes */
@@ -149,17 +160,16 @@ RelationBuildPartitionKey(Relation relation)
 		expr = eval_const_expressions(NULL, expr);
 		fix_opfuncids(expr);
 
-		oldcxt = MemoryContextSwitchTo(partkeycxt);
+		oldcxt = MemoryContextSwitchTo(relation->rd_partcxt);
 		key->partexprs = (List *) copyObject(expr);
 		MemoryContextSwitchTo(oldcxt);
 	}
 
-	oldcxt = MemoryContextSwitchTo(partkeycxt);
+	oldcxt = MemoryContextSwitchTo(relation->rd_partcxt);
 	key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
 	key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
 	key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
-	key->partsupfunc = (FmgrInfo *) palloc0(key->partnatts * sizeof(FmgrInfo));
-
+	key->partsupfuncid = (Oid *) palloc0(key->partnatts * sizeof(Oid));
 	key->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid));
 
 	/* Gather type and collation info as well */
@@ -169,6 +179,13 @@ RelationBuildPartitionKey(Relation relation)
 	key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
 	key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
 	key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+
+	/*
+	 * Also allocate space for partition support procedure FmgrInfo's, but
+	 * they won't be filled until somebody calls partition_get_procinfo.
+	 */
+	relation->rd_partsupfunc = (FmgrInfo *)
+						palloc0(key->partnatts * sizeof(FmgrInfo));
 	MemoryContextSwitchTo(oldcxt);
 
 	/* determine support function number to search for */
@@ -210,7 +227,7 @@ RelationBuildPartitionKey(Relation relation)
 							procnum,
 							format_type_be(opclassform->opcintype))));
 
-		fmgr_info_cxt(funcid, &key->partsupfunc[i], partkeycxt);
+		key->partsupfuncid[i] = funcid;
 
 		/* Collation */
 		key->partcollation[i] = collation->values[i];
@@ -244,13 +261,6 @@ RelationBuildPartitionKey(Relation relation)
 	}
 
 	ReleaseSysCache(tuple);
-
-	/*
-	 * Success --- reparent our context and make the relcache point to the
-	 * newly constructed key
-	 */
-	MemoryContextSetParent(partkeycxt, CacheMemoryContext);
-	relation->rd_partkeycxt = partkeycxt;
 	relation->rd_partkey = key;
 }
 
@@ -349,6 +359,20 @@ RelationBuildPartitionDesc(Relation rel)
 
 	if (nparts > 0)
 	{
+		FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
+		PartitionBoundSortInfo sortinfo;
+
+		/* Get partsupfunc FmgrInfo's. */
+		for (i = 0; i < key->partnatts; i++)
+		{
+			fmgr_info_copy(&partsupfunc[i],
+						   partition_getprocinfo(rel, key, i),
+						   CurrentMemoryContext);
+		}
+
+		sortinfo.partsupfunc = partsupfunc;
+		sortinfo.key = key;
+
 		oids = (Oid *) palloc(nparts * sizeof(Oid));
 		i = 0;
 		foreach(cell, partoids)
@@ -466,7 +490,7 @@ RelationBuildPartitionDesc(Relation rel)
 			}
 
 			qsort_arg(all_values, ndatums, sizeof(PartitionListValue *),
-					  qsort_partition_list_value_cmp, (void *) key);
+					  qsort_partition_list_value_cmp, (void *) &sortinfo);
 		}
 		else if (key->strategy == PARTITION_STRATEGY_RANGE)
 		{
@@ -519,7 +543,7 @@ RelationBuildPartitionDesc(Relation rel)
 			qsort_arg(all_bounds, ndatums,
 					  sizeof(PartitionRangeBound *),
 					  qsort_partition_rbound_cmp,
-					  (void *) key);
+					  (void *) &sortinfo);
 
 			/* Save distinct bounds from all_bounds into rbounds. */
 			rbounds = (PartitionRangeBound **)
@@ -551,7 +575,7 @@ RelationBuildPartitionDesc(Relation rel)
 					if (cur->kind[j] != PARTITION_RANGE_DATUM_VALUE)
 						break;
 
-					cmpval = FunctionCall2Coll(&key->partsupfunc[j],
+					cmpval = FunctionCall2Coll(&partsupfunc[j],
 											   key->partcollation[j],
 											   cur->datums[j],
 											   prev->datums[j]);
@@ -582,12 +606,7 @@ RelationBuildPartitionDesc(Relation rel)
 	}
 
 	/* Now build the actual relcache partition descriptor */
-	rel->rd_pdcxt = AllocSetContextCreate(CacheMemoryContext,
-										  "partition descriptor",
-										  ALLOCSET_DEFAULT_SIZES);
-	MemoryContextCopyAndSetIdentifier(rel->rd_pdcxt, RelationGetRelationName(rel));
-
-	oldcxt = MemoryContextSwitchTo(rel->rd_pdcxt);
+	oldcxt = MemoryContextSwitchTo(rel->rd_partcxt);
 
 	result = (PartitionDescData *) palloc0(sizeof(PartitionDescData));
 	result->nparts = nparts;
@@ -794,6 +813,107 @@ RelationBuildPartitionDesc(Relation rel)
 	rel->rd_partdesc = result;
 }
 
+/*
+ * Functions to get a copy of partitioning infomation cached in RelationData.
+ */
+
+PartitionKey
+RelationGetPartitionKey(Relation relation)
+{
+	PartitionKey partkey = relation->rd_partkey;
+
+	return partkey ? partition_key_copy(partkey) : NULL;
+}
+
+int
+RelationGetPartitionCount(Relation relation)
+{
+	PartitionDesc partdesc = relation->rd_partdesc;
+
+	return partdesc->nparts;
+}
+
+Oid *
+RelationGetPartitionOids(Relation relation)
+{
+	PartitionDesc partdesc = relation->rd_partdesc;
+	Oid *result = NULL;
+
+	Assert(partdesc != NULL);
+	if (partdesc->nparts > 0)
+	{
+		result = palloc(partdesc->nparts * sizeof(Oid));
+		memcpy(result, partdesc->oids, partdesc->nparts * sizeof(Oid));
+	}
+
+	return result;
+}
+
+PartitionBoundInfo
+RelationGetPartitionBounds(Relation relation)
+{
+	PartitionDesc partdesc = relation->rd_partdesc;
+
+	return partdesc->boundinfo
+				? partition_bounds_copy(partdesc->boundinfo,
+										relation->rd_partkey)
+				: NULL;
+}
+
+/*
+ * RelationGetDefaultPartitionOid
+ *
+ * Return the OID of the default partition, if one exists; else InvalidOid.
+ */
+Oid
+RelationGetDefaultPartitionOid(Relation rel)
+{
+	PartitionBoundInfo boundinfo;
+
+	/* Shouldn't be here otherwise! */
+	Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+	boundinfo = rel->rd_partdesc->boundinfo;
+
+	if (boundinfo && partition_bound_has_default(boundinfo))
+		return rel->rd_partdesc->oids[boundinfo->default_index];
+
+	return InvalidOid;
+}
+
+/*
+ * partition_getprocinfo
+ *		Return fmgr lookup info of partition support procs from relcache
+ *
+ * If it's not been built yet by calling fmgr.c, do that and add it to
+ * relcache.
+ */
+FmgrInfo *
+partition_getprocinfo(Relation rel, PartitionKey key, int partattoff)
+{
+	FmgrInfo   *info;
+
+	info = rel->rd_partsupfunc;
+
+	Assert(info != NULL);
+
+	info += partattoff;
+
+	/* Initialize the lookup info if first time through */
+	if (info->fn_oid == InvalidOid)
+	{
+		RegProcedure *func = key->partsupfuncid;
+		RegProcedure procId;
+
+		Assert(func != NULL);
+
+		procId = func[partattoff];
+		Assert(RegProcedureIsValid(procId));
+		fmgr_info_cxt(procId, info, rel->rd_partcxt);
+	}
+
+	return info;
+}
+
 /*
  * RelationGetPartitionQual
  *
@@ -867,9 +987,10 @@ qsort_partition_list_value_cmp(const void *a, const void *b, void *arg)
 {
 	Datum		val1 = (*(const PartitionListValue **) a)->value,
 				val2 = (*(const PartitionListValue **) b)->value;
-	PartitionKey key = (PartitionKey) arg;
+	PartitionBoundSortInfo *sortinfo = (PartitionBoundSortInfo *) arg;
+	PartitionKey key = sortinfo->key;
 
-	return DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
+	return DatumGetInt32(FunctionCall2Coll(&sortinfo->partsupfunc[0],
 										   key->partcollation[0],
 										   val1, val2));
 }
@@ -880,13 +1001,70 @@ qsort_partition_rbound_cmp(const void *a, const void *b, void *arg)
 {
 	PartitionRangeBound *b1 = (*(PartitionRangeBound *const *) a);
 	PartitionRangeBound *b2 = (*(PartitionRangeBound *const *) b);
-	PartitionKey key = (PartitionKey) arg;
+	PartitionBoundSortInfo *sortinfo = (PartitionBoundSortInfo *) arg;
+	PartitionKey key = sortinfo->key;
 
-	return partition_rbound_cmp(key->partnatts, key->partsupfunc,
+	return partition_rbound_cmp(key->partnatts, sortinfo->partsupfunc,
 								key->partcollation, b1->datums, b1->kind,
 								b1->lower, b2);
 }
 
+/*
+ * partition_key_copy
+ *
+ * The copy is allocated in the current memory context.
+ */
+static PartitionKey
+partition_key_copy(PartitionKey fromkey)
+{
+	PartitionKey newkey;
+	int			n;
+
+	Assert(fromkey != NULL);
+
+	newkey = (PartitionKey) palloc(sizeof(PartitionKeyData));
+
+	newkey->strategy = fromkey->strategy;
+	newkey->partnatts = n = fromkey->partnatts;
+
+	newkey->partattrs = (AttrNumber *) palloc(n * sizeof(AttrNumber));
+	memcpy(newkey->partattrs, fromkey->partattrs, n * sizeof(AttrNumber));
+
+	newkey->partexprs = copyObject(fromkey->partexprs);
+
+	newkey->partopfamily = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->partopfamily, fromkey->partopfamily, n * sizeof(Oid));
+
+	newkey->partopcintype = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->partopcintype, fromkey->partopcintype, n * sizeof(Oid));
+
+	newkey->partsupfuncid = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->partsupfuncid, fromkey->partsupfuncid, n * sizeof(Oid));
+
+	newkey->partcollation = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->partcollation, fromkey->partcollation, n * sizeof(Oid));
+
+	newkey->parttypid = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->parttypid, fromkey->parttypid, n * sizeof(Oid));
+
+	newkey->parttypmod = (int32 *) palloc(n * sizeof(int32));
+	memcpy(newkey->parttypmod, fromkey->parttypmod, n * sizeof(int32));
+
+	newkey->parttyplen = (int16 *) palloc(n * sizeof(int16));
+	memcpy(newkey->parttyplen, fromkey->parttyplen, n * sizeof(int16));
+
+	newkey->parttypbyval = (bool *) palloc(n * sizeof(bool));
+	memcpy(newkey->parttypbyval, fromkey->parttypbyval, n * sizeof(bool));
+
+	newkey->parttypalign = (char *) palloc(n * sizeof(bool));
+	memcpy(newkey->parttypalign, fromkey->parttypalign, n * sizeof(char));
+
+	newkey->parttypcoll = (Oid *) palloc(n * sizeof(Oid));
+	memcpy(newkey->parttypcoll, fromkey->parttypcoll, n * sizeof(Oid));
+
+	return newkey;
+}
+
 /*
  * generate_partition_qual
  *
@@ -960,7 +1138,7 @@ generate_partition_qual(Relation rel)
 		elog(ERROR, "unexpected whole-row reference found in partition key");
 
 	/* Save a copy in the relcache */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+	oldcxt = MemoryContextSwitchTo(rel->rd_partcxt);
 	rel->rd_partcheck = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 5c17fe1742..aed32406f1 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -43,7 +43,6 @@
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
-#include "catalog/partition.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_attrdef.h"
@@ -286,9 +285,6 @@ static OpClassCacheEnt *LookupOpclassInfo(Oid operatorClassOid,
 				  StrategyNumber numSupport);
 static void RelationCacheInitFileRemoveInDir(const char *tblspcpath);
 static void unlink_initfile(const char *initfilename);
-static bool equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1,
-					PartitionDesc partdesc2);
-
 
 /*
  *		ScanPgRelation
@@ -1000,60 +996,6 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
 	return true;
 }
 
-/*
- * equalPartitionDescs
- *		Compare two partition descriptors for logical equality
- */
-static bool
-equalPartitionDescs(PartitionKey key, PartitionDesc partdesc1,
-					PartitionDesc partdesc2)
-{
-	int			i;
-
-	if (partdesc1 != NULL)
-	{
-		if (partdesc2 == NULL)
-			return false;
-		if (partdesc1->nparts != partdesc2->nparts)
-			return false;
-
-		Assert(key != NULL || partdesc1->nparts == 0);
-
-		/*
-		 * Same oids? If the partitioning structure did not change, that is,
-		 * no partitions were added or removed to the relation, the oids array
-		 * should still match element-by-element.
-		 */
-		for (i = 0; i < partdesc1->nparts; i++)
-		{
-			if (partdesc1->oids[i] != partdesc2->oids[i])
-				return false;
-		}
-
-		/*
-		 * Now compare partition bound collections.  The logic to iterate over
-		 * the collections is private to partition.c.
-		 */
-		if (partdesc1->boundinfo != NULL)
-		{
-			if (partdesc2->boundinfo == NULL)
-				return false;
-
-			if (!partition_bounds_equal(key->partnatts, key->parttyplen,
-										key->parttypbyval,
-										partdesc1->boundinfo,
-										partdesc2->boundinfo))
-				return false;
-		}
-		else if (partdesc2->boundinfo != NULL)
-			return false;
-	}
-	else if (partdesc2 != NULL)
-		return false;
-
-	return true;
-}
-
 /*
  *		RelationBuildDesc
  *
@@ -1182,18 +1124,40 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	relation->rd_fkeylist = NIL;
 	relation->rd_fkeyvalid = false;
 
-	/* if a partitioned table, initialize key and partition descriptor info */
-	if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+	/*
+	 * If we need to initialize partitioning info, do that in a dedicated
+	 * context that's attached to the cache context.
+	 */
+	if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
+		relation->rd_rel->relispartition)
 	{
-		RelationBuildPartitionKey(relation);
-		RelationBuildPartitionDesc(relation);
+		relation->rd_partcxt = AllocSetContextCreate(CacheMemoryContext,
+													 "partition info",
+													 ALLOCSET_SMALL_SIZES);
+		MemoryContextCopyAndSetIdentifier(relation->rd_partcxt,
+										  RelationGetRelationName(relation));
+
+		/*
+		 * For a partitioned table, initialize partition key and partition
+		 * descriptor.
+		 */
+		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		{
+			RelationBuildPartitionKey(relation);
+			RelationBuildPartitionDesc(relation);
+		}
+
+		/*
+		 * Partition constraint of a partition itself is built only when
+		 * needed.  See RelationGetPartitionQual().
+		 */
 	}
 	else
 	{
-		relation->rd_partkeycxt = NULL;
+		relation->rd_partcxt = NULL;
 		relation->rd_partkey = NULL;
 		relation->rd_partdesc = NULL;
-		relation->rd_pdcxt = NULL;
+		relation->rd_partcheck = NIL;
 	}
 
 	/*
@@ -2196,12 +2160,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 		MemoryContextDelete(relation->rd_rulescxt);
 	if (relation->rd_rsdesc)
 		MemoryContextDelete(relation->rd_rsdesc->rscxt);
-	if (relation->rd_partkeycxt)
-		MemoryContextDelete(relation->rd_partkeycxt);
-	if (relation->rd_pdcxt)
-		MemoryContextDelete(relation->rd_pdcxt);
-	if (relation->rd_partcheck)
-		pfree(relation->rd_partcheck);
+	if (relation->rd_partcxt)
+		MemoryContextDelete(relation->rd_partcxt);
 	if (relation->rd_fdwroutine)
 		pfree(relation->rd_fdwroutine);
 	pfree(relation);
@@ -2368,8 +2328,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 		bool		keep_tupdesc;
 		bool		keep_rules;
 		bool		keep_policies;
-		bool		keep_partkey;
-		bool		keep_partdesc;
 
 		/* Build temporary entry, but don't link it into hashtable */
 		newrel = RelationBuildDesc(save_relid, false);
@@ -2400,10 +2358,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 		keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
 		keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
 		keep_policies = equalRSDesc(relation->rd_rsdesc, newrel->rd_rsdesc);
-		keep_partkey = (relation->rd_partkey != NULL);
-		keep_partdesc = equalPartitionDescs(relation->rd_partkey,
-											relation->rd_partdesc,
-											newrel->rd_partdesc);
 
 		/*
 		 * Perform swapping of the relcache entry contents.  Within this
@@ -2458,18 +2412,6 @@ RelationClearRelation(Relation relation, bool rebuild)
 		SWAPFIELD(Oid, rd_toastoid);
 		/* pgstat_info must be preserved */
 		SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
-		/* partition key must be preserved, if we have one */
-		if (keep_partkey)
-		{
-			SWAPFIELD(PartitionKey, rd_partkey);
-			SWAPFIELD(MemoryContext, rd_partkeycxt);
-		}
-		/* preserve old partdesc if no logical change */
-		if (keep_partdesc)
-		{
-			SWAPFIELD(PartitionDesc, rd_partdesc);
-			SWAPFIELD(MemoryContext, rd_pdcxt);
-		}
 
 #undef SWAPFIELD
 
@@ -3707,19 +3649,11 @@ RelationCacheInitializePhase3(void)
 		/*
 		 * Reload the partition key and descriptor for a partitioned table.
 		 */
-		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
-			relation->rd_partkey == NULL)
+		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 		{
 			RelationBuildPartitionKey(relation);
-			Assert(relation->rd_partkey != NULL);
-
-			restart = true;
-		}
-
-		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
-			relation->rd_partdesc == NULL)
-		{
 			RelationBuildPartitionDesc(relation);
+			Assert(relation->rd_partkey != NULL);
 			Assert(relation->rd_partdesc != NULL);
 
 			restart = true;
@@ -5572,9 +5506,9 @@ load_relcache_init_file(bool shared)
 		rel->rd_rulescxt = NULL;
 		rel->trigdesc = NULL;
 		rel->rd_rsdesc = NULL;
-		rel->rd_partkeycxt = NULL;
+		rel->rd_partcxt = NULL;
 		rel->rd_partkey = NULL;
-		rel->rd_pdcxt = NULL;
+		rel->rd_partsupfunc = NULL;
 		rel->rd_partdesc = NULL;
 		rel->rd_partcheck = NIL;
 		rel->rd_indexprs = NIL;
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 6ed013380b..6e468630b5 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -16,25 +16,12 @@
 #include "fmgr.h"
 #include "executor/tuptable.h"
 #include "nodes/execnodes.h"
-#include "partitioning/partbounds.h"
 #include "parser/parse_node.h"
 #include "utils/rel.h"
 
 /* Seed for the extended hash function */
 #define HASH_PARTITION_SEED UINT64CONST(0x7A5B22367996DCFD)
 
-/*
- * Information about partitions of a partitioned table.
- */
-typedef struct PartitionDescData
-{
-	int			nparts;			/* Number of partitions */
-	Oid		   *oids;			/* OIDs of partitions */
-	PartitionBoundInfo boundinfo;	/* collection of partition bounds */
-} PartitionDescData;
-
-typedef struct PartitionDescData *PartitionDesc;
-
 extern Oid	get_partition_parent(Oid relid);
 extern List *get_partition_ancestors(Oid relid);
 extern List *map_partition_varattnos(List *expr, int fromrel_varno,
@@ -42,8 +29,6 @@ extern List *map_partition_varattnos(List *expr, int fromrel_varno,
 						bool *found_whole_row);
 extern bool has_partition_attrs(Relation rel, Bitmapset *attnums,
 					bool *used_in_expr);
-
-extern Oid	get_default_oid_from_partdesc(PartitionDesc partdesc);
 extern Oid	get_default_partition_oid(Oid parentId);
 extern void update_default_partition_oid(Oid parentId, Oid defaultPartId);
 extern List *get_proposed_default_constraint(List *new_part_constaints);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 70ee3da76b..eb3fd8a206 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -17,8 +17,8 @@
 #include "access/htup.h"
 #include "catalog/dependency.h"
 #include "catalog/objectaddress.h"
-#include "nodes/parsenodes.h"
 #include "catalog/partition.h"
+#include "nodes/parsenodes.h"
 #include "storage/lock.h"
 #include "utils/relcache.h"
 
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index e81bdc4a0a..47eedb91bb 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -25,14 +25,16 @@
  *	reldesc		Relation descriptor of the table
  *	key			Partition key information of the table
  *	keystate	Execution state required for expressions in the partition key
- *	partdesc	Partition descriptor of the table
+ *	partsupfunc		fmgr lookup info of partition support functions
+ *	nparts		Number of partitions of the table
+ *	boundinfo	PartitionBoundInfo of the table
  *	tupslot		A standalone TupleTableSlot initialized with this table's tuple
  *				descriptor
  *	tupmap		TupleConversionMap to convert from the parent's rowtype to
  *				this table's rowtype (when extracting the partition key of a
  *				tuple just before routing it through this table)
- *	indexes		Array with partdesc->nparts members (for details on what
- *				individual members represent, see how they are set in
+ *	indexes		Array with nparts members (for details on what individual
+ *				members represent, see how they are set in
  *				get_partition_dispatch_recurse())
  *-----------------------
  */
@@ -41,7 +43,9 @@ typedef struct PartitionDispatchData
 	Relation	reldesc;
 	PartitionKey key;
 	List	   *keystate;		/* list of ExprState */
-	PartitionDesc partdesc;
+	FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
+	int			nparts;
+	PartitionBoundInfo boundinfo;
 	TupleTableSlot *tupslot;
 	TupleConversionMap *tupmap;
 	int		   *indexes;
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index 8bae4b2642..3e5f9dd920 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -11,7 +11,6 @@
 #ifndef PARTBOUNDS_H
 #define PARTBOUNDS_H
 
-#include "nodes/parsenodes.h"
 #include "utils/partcache.h"
 
 /*
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h
index 2ae2fd16ed..5c44d9d212 100644
--- a/src/include/partitioning/partprune.h
+++ b/src/include/partitioning/partprune.h
@@ -14,7 +14,7 @@
 #ifndef PARTPRUNE_H
 #define PARTPRUNE_H
 
-#include "catalog/partition.h"
+#include "partitioning/partbounds.h"
 #include "nodes/relation.h"
 
 /*
@@ -30,7 +30,7 @@ typedef struct PartitionPruneContext
 	Oid		   *partopfamily;
 	Oid		   *partopcintype;
 	Oid		   *partcollation;
-	FmgrInfo   *partsupfunc;
+	FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
 
 	/* Number of partitions */
 	int			nparts;
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
index b9bde9fb7b..2a6f1ad700 100644
--- a/src/include/utils/partcache.h
+++ b/src/include/utils/partcache.h
@@ -27,7 +27,7 @@ typedef struct PartitionKeyData
 
 	Oid		   *partopfamily;	/* OIDs of operator families */
 	Oid		   *partopcintype;	/* OIDs of opclass declared input data types */
-	FmgrInfo   *partsupfunc;	/* lookup info for support funcs */
+	Oid		   *partsupfuncid;	/* partition support func OIDs */
 
 	/* Partitioning collation per attribute */
 	Oid		   *partcollation;
@@ -43,8 +43,23 @@ typedef struct PartitionKeyData
 
 typedef struct PartitionKeyData *PartitionKey;
 
+/*
+ * PartitionBoundInfo encapsulates a set of partition bounds.  It is usually
+ * associated with partitioned tables as part of its partition descriptor.
+ *
+ * The internal structure appears in partbounds.h.
+ */
+typedef struct PartitionBoundInfoData *PartitionBoundInfo;
+
 extern void RelationBuildPartitionKey(Relation relation);
 extern void RelationBuildPartitionDesc(Relation rel);
+extern PartitionKey RelationGetPartitionKey(Relation relation);
+extern FmgrInfo *partition_getprocinfo(Relation rel, PartitionKey key,
+									   int partattoff);
+extern int RelationGetPartitionCount(Relation relation);
+extern Oid *RelationGetPartitionOids(Relation relation);
+extern PartitionBoundInfo RelationGetPartitionBounds(Relation relation);
+extern Oid	RelationGetDefaultPartitionOid(Relation rel);
 extern List *RelationGetPartitionQual(Relation rel);
 extern Expr *get_partition_qual_relid(Oid relid);
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index bc74d04ce5..2c23704710 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -96,9 +96,11 @@ typedef struct RelationData
 	List	   *rd_fkeylist;	/* list of ForeignKeyCacheInfo (see below) */
 	bool		rd_fkeyvalid;	/* true if list has been computed */
 
-	MemoryContext rd_partkeycxt;	/* private memory cxt for the below */
+	MemoryContext rd_partcxt;	/* private memory cxt for the values contained
+								 * in the fields related to partitioning */
 	struct PartitionKeyData *rd_partkey;	/* partition key, or NULL */
-	MemoryContext rd_pdcxt;		/* private context for partdesc */
+	FmgrInfo   *rd_partsupfunc;	/* lookup info for partition support
+									 * procs */
 	struct PartitionDescData *rd_partdesc;	/* partitions, or NULL */
 	List	   *rd_partcheck;	/* partition CHECK quals */
 
@@ -218,6 +220,21 @@ typedef struct ForeignKeyCacheInfo
 	Oid			conpfeqop[INDEX_MAX_KEYS];	/* PK = FK operator OIDs */
 } ForeignKeyCacheInfo;
 
+/*
+ * PartitionInfo
+ *		Information related to partitioning for a partitioned table
+ *
+ * This consists of the information from PartitionKey and PartitionDesc
+ * structs that are cached in the table's RelationData.
+ */
+typedef struct PartitionInfo
+{
+	PartitionKey	key;
+	int				nparts;
+	Oid			   *oids;
+	struct PartitionBoundInfoData *boundinfo;
+} PartitionInfo;
+
 /*
  * Options common for all all indexes
  */
@@ -583,12 +600,6 @@ typedef struct ViewOptions
 	 RelationNeedsWAL(relation) && \
 	 !IsCatalogRelation(relation))
 
-/*
- * RelationGetPartitionKey
- *		Returns the PartitionKey of a relation
- */
-#define RelationGetPartitionKey(relation) ((relation)->rd_partkey)
-
 /*
  * PartitionKey inquiry functions
  */
@@ -631,12 +642,6 @@ get_partition_col_typmod(PartitionKey key, int col)
 	return key->parttypmod[col];
 }
 
-/*
- * RelationGetPartitionDesc
- *		Returns partition descriptor for a relation.
- */
-#define RelationGetPartitionDesc(relation) ((relation)->rd_partdesc)
-
 /* routines in utils/cache/relcache.c */
 extern void RelationIncrementReferenceCount(Relation rel);
 extern void RelationDecrementReferenceCount(Relation rel);
-- 
2.15.1 (Apple Git-101)

