From 5a989e94e8d7e50a331740f7e7d2ccffed4e7391 Mon Sep 17 00:00:00 2001
From: "dgrowley@gmail.com" <dgrowley@gmail.com>
Date: Thu, 19 Apr 2018 11:51:16 +1200
Subject: [PATCH] Initialize expr states once in run-time partition pruning

Previously ExecInitExpr was called every time a Param needed to be evaulated
during run-time partition pruning.  This meant additional memory allocations
were made.  Here we change things so we now call this function just once
during the setup of run-time pruning.  We cache the result in
PartitionPruneContext so that it can be re-used each time pruning is invoked.
---
 src/backend/executor/execPartition.c | 31 +++++++++++++++++++++++++++++++
 src/backend/partitioning/partprune.c | 17 ++++++++++++-----
 src/include/partitioning/partprune.h |  6 ++++++
 3 files changed, 49 insertions(+), 5 deletions(-)

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 23a74bc3d9..5e4e24683c 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1459,7 +1459,9 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
 		PartitionDesc partdesc;
 		Relation	rel;
 		PartitionKey partkey;
+		ListCell   *lc2;
 		int			partnatts;
+		int			n_steps;
 
 		pprune->present_parts = bms_copy(pinfo->present_parts);
 		pprune->subnode_map = palloc(sizeof(int) * pinfo->nparts);
@@ -1482,6 +1484,7 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
 
 		partkey = RelationGetPartitionKey(rel);
 		partdesc = RelationGetPartitionDesc(rel);
+		n_steps = list_length(pinfo->pruning_steps);
 
 		context->strategy = partkey->strategy;
 		context->partnatts = partnatts = partkey->partnatts;
@@ -1493,6 +1496,34 @@ ExecSetupPartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
 		context->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
 		context->planstate = planstate;
 		context->safeparams = NULL; /* empty for now */
+		context->exprstates = palloc0(sizeof(ExprState *) * n_steps * partnatts);
+
+		/* Initialize expression states for each expression */
+		foreach(lc2, pinfo->pruning_steps)
+		{
+			PartitionPruneStepOp *step = (PartitionPruneStepOp *) lfirst(lc2);
+			ListCell   *lc3;
+			int			keyno;
+
+			if (!IsA(step, PartitionPruneStepOp))
+				continue;
+
+			Assert(list_length(step->exprs) <= partnatts);
+
+			keyno = 0;
+			foreach(lc3, step->exprs)
+			{
+				Expr	   *expr = (Expr *) lfirst(lc3);
+
+				/*
+				 * partkey_datum_from_expr does not need an expression state
+				 * to evaluate a Const.
+				 */
+				if (!IsA(expr, Const))
+					context->exprstates[step->step.step_id * partnatts + keyno] = ExecInitExpr(expr, context->planstate);
+				keyno++;
+			}
+		}
 
 		pprune->pruning_steps = pinfo->pruning_steps;
 		pprune->extparams = bms_copy(pinfo->extparams);
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 7666c6c412..f688357fe4 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -169,7 +169,8 @@ static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *cont
 static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause,
 							   Expr *partkey, Expr **outconst);
 static bool partkey_datum_from_expr(PartitionPruneContext *context,
-						Expr *expr, Datum *value);
+						int step_id, int keyno, int partnatts, Expr *expr,
+						Datum *value);
 
 /*
  * make_partition_pruneinfo
@@ -444,6 +445,7 @@ prune_append_rel_partitions(RelOptInfo *rel)
 	/* Not valid when being called from the planner */
 	context.planstate = NULL;
 	context.safeparams = NULL;
+	context.exprstates = NULL;
 
 	/* Actual pruning happens here. */
 	partindexes = get_matching_partitions(&context, pruning_steps);
@@ -2785,7 +2787,8 @@ perform_pruning_base_step(PartitionPruneContext *context,
 			Datum		datum;
 
 			expr = lfirst(lc1);
-			if (partkey_datum_from_expr(context, expr, &datum))
+			if (partkey_datum_from_expr(context, opstep->step.step_id, keyno,
+										context->partnatts, expr, &datum))
 			{
 				Oid			cmpfn;
 
@@ -3023,8 +3026,8 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
  *		evaluation was possible, otherwise false.
  */
 static bool
-partkey_datum_from_expr(PartitionPruneContext *context,
-						Expr *expr, Datum *value)
+partkey_datum_from_expr(PartitionPruneContext * context, int step_id,
+						int keyno, int partnatts, Expr *expr, Datum *value)
 {
 	switch (nodeTag(expr))
 	{
@@ -3044,7 +3047,11 @@ partkey_datum_from_expr(PartitionPruneContext *context,
 				ExprState  *exprstate;
 				bool		isNull;
 
-				exprstate = ExecInitExpr(expr, context->planstate);
+				Assert(context->exprstates);
+
+				exprstate = context->exprstates[step_id * partnatts + keyno];
+
+				Assert(exprstate);
 
 				*value = ExecEvalExprSwitchContext(exprstate,
 												   context->planstate->ps_ExprContext,
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h
index a5568abce6..31a4592397 100644
--- a/src/include/partitioning/partprune.h
+++ b/src/include/partitioning/partprune.h
@@ -50,6 +50,12 @@ typedef struct PartitionPruneContext
 	 * are not safe to use until the executor is running.
 	 */
 	Bitmapset  *safeparams;
+
+	/*
+	 * Array of ExprStates, one for each partkey in each pruning step.  Must
+	 * be allocated if planstate is non-NULL, otherwise can be NULL.
+	 */
+	ExprState **exprstates;
 } PartitionPruneContext;
 
 
-- 
2.16.2.windows.1

