0001-Implement-runtime-partiton-pruning.patch
application/octet-stream
Filename: 0001-Implement-runtime-partiton-pruning.patch
Type: application/octet-stream
Part: 0
Message:
Re: Runtime Partition Pruning
Patch
Format: format-patch
Series: patch 0001
Subject: Implement runtime partiton pruning
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 28 | 94 |
| src/backend/executor/nodeAppend.c | 300 | 15 |
| src/backend/nodes/copyfuncs.c | 27 | 0 |
| src/backend/nodes/readfuncs.c | 2 | 0 |
| src/backend/optimizer/path/allpaths.c | 60 | 12 |
| src/backend/optimizer/path/joinrels.c | 1 | 1 |
| src/backend/optimizer/plan/createplan.c | 46 | 0 |
| src/backend/optimizer/plan/planner.c | 1 | 1 |
| src/backend/optimizer/prep/prepunion.c | 2 | 2 |
| src/backend/optimizer/util/clauses.c | 16 | 0 |
| src/backend/optimizer/util/pathnode.c | 35 | 1 |
| src/backend/optimizer/util/relnode.c | 22 | 1 |
| src/include/catalog/partition.h | 8 | 0 |
| src/include/nodes/execnodes.h | 5 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/plannodes.h | 4 | 0 |
| src/include/nodes/relation.h | 87 | 0 |
| src/include/optimizer/clauses.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 1 | 1 |
| src/include/optimizer/paths.h | 4 | 0 |
From 6ee8c7bfe6c879f274e37c60d0e617d26dc01976 Mon Sep 17 00:00:00 2001
From: Beena Emerson <Beena.Emerson@EnterpriseDB.com>
Date: Thu, 9 Nov 2017 16:19:28 +0530
Subject: [PATCH] Implement runtime partiton pruning
Patch by: Beena Emerson, Dilip Kumar
Discussion: https://postgr.es/m/CAOG9ApE16ac-_VVZVvv0gePSgkg_BwYEV1NBqZFqDR2bBE0X0A@mail.gmail.com
---
src/backend/catalog/partition.c | 122 +++----------
src/backend/executor/nodeAppend.c | 315 ++++++++++++++++++++++++++++++--
src/backend/nodes/copyfuncs.c | 27 +++
src/backend/nodes/readfuncs.c | 2 +
src/backend/optimizer/path/allpaths.c | 72 ++++++--
src/backend/optimizer/path/joinrels.c | 2 +-
src/backend/optimizer/plan/createplan.c | 46 +++++
src/backend/optimizer/plan/planner.c | 2 +-
src/backend/optimizer/prep/prepunion.c | 4 +-
src/backend/optimizer/util/clauses.c | 16 ++
src/backend/optimizer/util/pathnode.c | 36 +++-
src/backend/optimizer/util/relnode.c | 23 ++-
src/backend/utils/cache/plancache.c | 2 +-
src/include/catalog/partition.h | 8 +
src/include/nodes/execnodes.h | 5 +
src/include/nodes/nodes.h | 1 +
src/include/nodes/plannodes.h | 4 +
src/include/nodes/relation.h | 87 +++++++++
src/include/optimizer/clauses.h | 1 +
src/include/optimizer/pathnode.h | 2 +-
src/include/optimizer/paths.h | 4 +
21 files changed, 652 insertions(+), 129 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 152ae60..073164d 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -130,84 +130,6 @@ typedef struct
} PartClause;
/*
- * PartScanKeyInfo
- * Bounding scan keys to look up a table's partitions obtained from
- * mutually-ANDed clauses containing partitioning-compatible operators
- */
-typedef struct PartScanKeyInfo
-{
- /*
- * Constants constituting the *whole* partition key compared using
- * partitioning-compatible equality operator(s). When n_eqkeys > 0, other
- * keys (minkeys and maxkeys) are irrelevant.
- */
- Datum eqkeys[PARTITION_MAX_KEYS];
- int n_eqkeys;
-
- /*
- * Constants that constitute the lower bound on the partition key or a
- * prefix thereof. The last of those constants is compared using > or >=
- * operator compatible with partitioning, making this the lower bound in
- * a range query.
- */
- Datum minkeys[PARTITION_MAX_KEYS];
- int n_minkeys;
- bool min_incl;
-
- /*
- * Constants that constitute the upper bound on the partition key or a
- * prefix thereof. The last of those constants is compared using < or <=
- * operator compatible with partitioning, making this the upper bound in
- * a range query.
- */
- Datum maxkeys[PARTITION_MAX_KEYS];
- int n_maxkeys;
- bool max_incl;
-
- /*
- * Specifies the type of NullTest that was applied to each of the
- * partition key columns or -1 if none was applied. Partitioning handles
- * null partition keys specially depending on the partitioning method in
- * use, so get_partitions_for_keys can return partitions according to
- * the nullness condition for partition keys.
- */
- NullTestType keynullness[PARTITION_MAX_KEYS];
-} PartScanKeyInfo;
-
- /* A data structure to represent a partition set. */
-typedef struct PartitionSet
-{
- /*
- * If either empty or all_parts is true, values of the other fields are
- * invalid.
- */
- bool empty; /* contains no partitions */
- bool all_parts; /* contains all partitions */
-
- /*
- * In the case of range partitioning, min_part_index contains the index of
- * the lowest partition contained in the set and max_datum_index that of
- * the highest partition (all partitions between these two indexes
- * inclusive are part of the set.) Since other types of partitioning do
- * not impose order on the data contained in successive partitions, these
- * fields are not set in that case.
- */
- bool use_range;
- int min_part_idx;
- int max_part_idx;
-
- /*
- * other_parts contains the indexes of partitions that are not covered by
- * the range defined by min/max indexes. For example, in the case of
- * range partitoning, it will include default partition index (if any).
- * Also, this is the only way to return list partitions, because list
- * partitions do not have the same ordering property as range partitions,
- * so it's pointless to use the min/max range method.
- */
- Bitmapset *other_parts;
-} PartitionSet;
-
-/*
* PartitionBoundCmpArg - Caller-defined argument to be passed to
* partition_bound_cmp()
*
@@ -277,9 +199,6 @@ static PartitionSet *partset_copy(const PartitionSet *in);
static PartitionSet *partset_intersect(PartitionSet *a, const PartitionSet *b);
static PartitionSet *partset_union(PartitionSet *a, const PartitionSet *b);
static PartitionSet *partset_new(bool empty, bool all_parts);
-static int classify_partition_bounding_keys(Relation relation, List *clauses,
- PartScanKeyInfo *keys, bool *constfalse,
- List **or_clauses);
static void remove_redundant_clauses(PartitionKey partkey,
int partattoff, List *all_clauses,
List **result, bool *constfalse);
@@ -287,8 +206,6 @@ static bool partition_cmp_args(Oid partopfamily, Oid partopcintype,
PartClause *op, PartClause *leftarg, PartClause *rightarg,
bool *result);
static bool partkey_datum_from_expr(const Expr *expr, Datum *value);
-static PartitionSet *get_partitions_for_keys(Relation rel,
- PartScanKeyInfo *keys);
/*
* RelationBuildPartitionDesc
@@ -1641,9 +1558,11 @@ get_partitions_from_clauses_guts(Relation relation, List *clauses)
List *or_clauses;
ListCell *lc;
+ memset(&keys, 0, sizeof(PartScanKeyInfo));
+
nkeys = classify_partition_bounding_keys(relation, clauses,
&keys, &constfalse,
- &or_clauses);
+ &or_clauses, false);
if (constfalse)
/* None of the partitions will satisfy the clauses. */
partset = partset_new(true, false);
@@ -1875,10 +1794,10 @@ partset_union(PartitionSet *a, const PartitionSet *b)
* the responsibility of the caller to process the argument clauses of each of
* the OR clauses, which would involve recursively calling this function.
*/
-static int
+int
classify_partition_bounding_keys(Relation relation, List *clauses,
PartScanKeyInfo *keys, bool *constfalse,
- List **or_clauses)
+ List **or_clauses, bool is_runtime)
{
PartitionKey partkey = RelationGetPartitionKey(relation);
int i;
@@ -2314,7 +2233,6 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
n_eqkeys = 0;
/* Populate keys. */
- memset(keys, 0, sizeof(PartScanKeyInfo));
if (n_eqkeys + n_minkeys + n_maxkeys + n_keynullness > 0)
{
Datum value;
@@ -2325,7 +2243,10 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
if (partkey_datum_from_expr(eqkey_exprs[i], &value))
{
- keys->eqkeys[i] = value;
+ if (value)
+ keys->eqkeys_datums[i] = value;
+ else
+ keys->eqkeys = lappend(keys->eqkeys, eqkey_exprs[i]);
n_datums_resolved++;
}
}
@@ -2337,7 +2258,10 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
if (partkey_datum_from_expr(minkey_exprs[i], &value))
{
- keys->minkeys[i] = value;
+ if (value)
+ keys->minkeys_datums[i] = value;
+ else
+ keys->minkeys = lappend(keys->minkeys, minkey_exprs[i]);
n_datums_resolved++;
}
}
@@ -2350,7 +2274,10 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
if (partkey_datum_from_expr(maxkey_exprs[i], &value))
{
- keys->maxkeys[i] = value;
+ if (value)
+ keys->maxkeys_datums[i] = value;
+ else
+ keys->maxkeys = lappend(keys->maxkeys, maxkey_exprs[i]);
n_datums_resolved++;
}
}
@@ -2363,6 +2290,9 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
n_total += n_keynullness;
}
+ if (n_total == 0)
+ keys = NULL;
+
return n_total;
}
@@ -2383,6 +2313,10 @@ partkey_datum_from_expr(const Expr *expr, Datum *value)
*value = ((Const *) expr)->constvalue;
return true;
+ case T_Param:
+ *value = '\0';
+ return true;
+
default:
return false;
}
@@ -2593,7 +2527,7 @@ partition_cmp_args(Oid partopfamily, Oid partopcintype,
* Outputs:
* Partition set satisfying the keys.
*/
-static PartitionSet *
+PartitionSet *
get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
{
PartitionSet *partset;
@@ -2677,7 +2611,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
if (keys->n_eqkeys > 0)
{
memset(&arg, 0, sizeof(PartitionBoundCmpArg));
- arg.datums = keys->eqkeys;
+ arg.datums = keys->eqkeys_datums;
arg.ndatums = keys->n_eqkeys;
eqoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
@@ -2729,7 +2663,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
if (keys->n_minkeys > 0)
{
memset(&arg, 0, sizeof(PartitionBoundCmpArg));
- arg.datums = keys->minkeys;
+ arg.datums = keys->minkeys_datums;
arg.ndatums = keys->n_minkeys;
minoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
@@ -2815,7 +2749,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
if (keys->n_maxkeys > 0)
{
memset(&arg, 0, sizeof(PartitionBoundCmpArg));
- arg.datums = keys->maxkeys;
+ arg.datums = keys->maxkeys_datums;
arg.ndatums = keys->n_maxkeys;
maxoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 1d2fb35..0aebc90 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -1,4 +1,4 @@
-/*-------------------------------------------------------------------------
+ /*-------------------------------------------------------------------------
*
* nodeAppend.c
* routines to handle append nodes.
@@ -57,9 +57,11 @@
#include "postgres.h"
+#include "nodes/relation.h"
#include "executor/execdebug.h"
#include "executor/nodeAppend.h"
#include "miscadmin.h"
+#include "optimizer/clauses.h"
static TupleTableSlot *ExecAppend(PlanState *pstate);
static bool exec_append_initialize_next(AppendState *appendstate);
@@ -108,6 +110,109 @@ exec_append_initialize_next(AppendState *appendstate)
}
/* ----------------------------------------------------------------
+ * extern_eval_const_expressions
+ *
+ * Evalute the expressions to a constant.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo*
+extern_eval_const_expressions(EState *estate, PartScanKeyInfo *old_keys)
+{
+ PartScanKeyInfo *keys;
+ int i;
+ ListCell *lc;
+
+ if (old_keys == NULL)
+ return NULL;
+
+ keys = makeNode(PartScanKeyInfo);
+ i = 0;
+ foreach(lc, old_keys->eqkeys)
+ {
+ Node *val = lfirst(lc);
+ Node *n = eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+ keys->eqkeys_datums[i++] = ((Const *) n)->constvalue;
+ }
+ keys->n_eqkeys = old_keys->n_eqkeys;
+
+ i = 0;
+ foreach(lc, old_keys->minkeys)
+ {
+ Node *val = lfirst(lc);
+ Node *n = eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+ keys->minkeys_datums[i++] = ((Const *) n)->constvalue;
+ }
+
+ keys->n_minkeys = old_keys->n_minkeys;
+ keys->min_incl = old_keys->min_incl;
+
+ i = 0;
+ foreach(lc, old_keys->maxkeys)
+ {
+ Node *val = lfirst(lc);
+ Node *n = eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+ keys->maxkeys_datums[i++] = ((Const *) n)->constvalue;
+ }
+ keys->n_maxkeys = old_keys->n_maxkeys;
+ keys->max_incl = old_keys->max_incl;
+
+ memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+
+ return keys;
+}
+
+
+/* ----------------------------------------------------------------
+ * exec_InitExpr
+ *
+ * Evaluate and store the ooutput of ExecInitExpr for each of the keys.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo *
+exec_InitExpr(PlanState *parent, PartScanKeyInfo *old_keys)
+{
+ PartScanKeyInfo *keys;
+ ListCell *lc;
+
+ if (old_keys == NULL)
+ return NULL;
+
+ keys = makeNode(PartScanKeyInfo);
+
+ foreach(lc, old_keys->eqkeys)
+ {
+ Expr *val = (Expr *) lfirst(lc);
+
+ keys->eqkeys = lappend(keys->eqkeys, ExecInitExpr(val, parent));
+ }
+ keys->n_eqkeys = old_keys->n_eqkeys;
+
+ foreach(lc, old_keys->minkeys)
+ {
+ Expr *val = (Expr *) lfirst(lc);
+
+ keys->minkeys = lappend(keys->minkeys, ExecInitExpr(val, parent));
+ }
+ keys->n_minkeys = old_keys->n_minkeys;
+ keys->min_incl = old_keys->min_incl;
+
+ foreach(lc, old_keys->maxkeys)
+ {
+ Expr *val = (Expr *) lfirst(lc);
+
+ keys->maxkeys = lappend(keys->maxkeys, ExecInitExpr(val, parent));
+ }
+ keys->n_maxkeys = old_keys->n_maxkeys;
+ keys->max_incl = old_keys->max_incl;
+
+ memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+ return keys;
+}
+
+/* ----------------------------------------------------------------
* ExecInitAppend
*
* Begin all of the subscans of the append node.
@@ -165,22 +270,97 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlot(estate, &appendstate->ps);
- /*
- * call ExecInitNode on each of the plans to be executed and save the
- * results into the array "appendplans".
- */
- i = 0;
- foreach(lc, node->appendplans)
+ if (node->et_keys)
{
- Plan *initNode = (Plan *) lfirst(lc);
+ List *temp_appendplans = NIL;
+ Append *temp_node;
+ Relation rel;
+ PartScanKeyInfo *keys;
+ PartitionSet *partset;
+ int i;
+
+ keys = extern_eval_const_expressions(estate, node->et_keys);
+
+ rel = relation_open(node->parentoid, NoLock);
+ partset = get_partitions_for_keys(rel, keys);
+ relation_close(rel, NoLock);
- appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
- i++;
+ if (!partset->empty)
+ {
+ if (partset->all_parts)
+ {
+ i = 0;
+ foreach(lc, node->appendplans)
+ {
+ Plan *initNode = (Plan *) lfirst(lc);
+
+ temp_appendplans = lappend(temp_appendplans, initNode);
+
+ appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
+ i++;
+ }
+ }
+ else
+ {
+ int j;
+
+ i = 0;
+ if (partset->use_range)
+ {
+ for (j = partset->min_part_idx; j <= partset->max_part_idx; j++)
+ {
+ Plan *initNode = (Plan *) list_nth(node->appendplans, j);
+
+ temp_appendplans = lappend(temp_appendplans, initNode);
+ appendplanstates[i++] = ExecInitNode(initNode, estate, eflags);
+ }
+ }
+
+ if (!bms_is_empty(partset->other_parts))
+ {
+ while ((j = bms_first_member(partset->other_parts)) >= 0)
+ {
+ Plan *initNode = (Plan *) list_nth(node->appendplans, j);
+
+ temp_appendplans = lappend(temp_appendplans, initNode);
+ appendplanstates[i++] = ExecInitNode(initNode, estate, eflags);
+ }
+ }
+ }
+ }
+ /* create new AppendState for our append node */
+ temp_node = copyObject(node);
+ temp_node->appendplans = temp_appendplans;
+ ((Plan *) temp_node)->plan_rows = i;
+ appendstate->as_nplans = i;
+ appendstate->ps.plan = (Plan *) temp_node;
+ appendstate->ps.state = estate;
+ appendstate->ps.ExecProcNode = ExecAppend;
+ appendstate->appendplans = (PlanState **) palloc0(i * sizeof(PlanState *));
+ appendstate->appendplans = appendplanstates;
}
+ else
+ {
+ appendstate->parentoid = node->parentoid;
+ ExecAssignExprContext(estate, &appendstate->ps);
+
+ /*
+ * call ExecInitNode on each of the plans to be executed and save the
+ * results into the array "appendplans".
+ */
+ i = 0;
+ foreach(lc, node->appendplans)
+ {
+ Plan *initNode = (Plan *) lfirst(lc);
+
+ appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
+ i++;
+ }
+ }
+
+ if (node->ex_keys)
+ appendstate->ex_keys = exec_InitExpr((PlanState *) appendstate, node->ex_keys);
- /*
- * initialize output tuple type
- */
ExecAssignResultTypeFromTL(&appendstate->ps);
appendstate->ps.ps_ProjInfo = NULL;
@@ -237,7 +417,21 @@ ExecAppend(PlanState *pstate)
* ExecInitAppend.
*/
if (ScanDirectionIsForward(node->ps.state->es_direction))
- node->as_whichplan++;
+ {
+ /*
+ * For runtime partition pruning, goto the next valid partition
+ * index
+ */
+ if (node->index)
+ {
+ if (++node->as_whichpartition < list_length(node->index))
+ node->as_whichplan = list_nth_int(node->index, node->as_whichpartition);
+
+ return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+ }
+ else
+ node->as_whichplan++;
+ }
else
node->as_whichplan--;
if (!exec_append_initialize_next(node))
@@ -275,11 +469,95 @@ ExecEndAppend(AppendState *node)
ExecEndNode(appendplans[i]);
}
+/* ----------------------------------------------------------------
+ * exec_EvalExpr
+ *
+ * Convert the expressions to a constant.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo*
+exec_EvalExpr(ExprContext *econtext, PartScanKeyInfo *old_keys)
+{
+ PartScanKeyInfo *keys;
+ int i;
+ bool isnull;
+ ListCell *lc;
+
+ if (old_keys == NULL)
+ return NULL;
+
+ keys = makeNode(PartScanKeyInfo);
+ i = 0;
+ foreach(lc, old_keys->eqkeys)
+ {
+ ExprState *val = lfirst(lc);
+
+ keys->eqkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+ }
+ keys->n_eqkeys = old_keys->n_eqkeys;
+
+ i = 0;
+ foreach(lc, old_keys->minkeys)
+ {
+ ExprState *val = lfirst(lc);
+
+ keys->minkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+ }
+ keys->n_minkeys = old_keys->n_minkeys;
+ keys->min_incl = old_keys->min_incl;
+
+ i = 0;
+ foreach(lc, old_keys->maxkeys)
+ {
+ ExprState *val = lfirst(lc);
+
+ keys->maxkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+ }
+ keys->n_maxkeys = old_keys->n_maxkeys;
+ keys->max_incl = old_keys->max_incl;
+
+ memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+ return keys;
+}
+
void
ExecReScanAppend(AppendState *node)
{
int i;
+ if (node->ps.chgParam != NULL && node->ex_keys)
+ {
+ PartitionSet *partset;
+
+ node->index = NULL;
+ node->ex_keys = exec_EvalExpr(node->ps.ps_ExprContext, node->ex_keys);
+ if (node->parentoid)
+ {
+ Relation rel = relation_open(node->parentoid, NoLock);
+
+ partset = get_partitions_for_keys(rel, node->ex_keys);
+ relation_close(rel, NoLock);
+ }
+ if (!partset->empty)
+ {
+ if (!partset->all_parts)
+ {
+ int j;
+
+ i = 0;
+ if (partset->use_range)
+ {
+ for (j = partset->min_part_idx; j <= partset->max_part_idx; j++)
+ node->index = lappend_int(node->index, j);
+ }
+ if (!bms_is_empty(partset->other_parts))
+ {
+ while ((j = bms_first_member(partset->other_parts)) >= 0)
+ node->index = lappend_int(node->index, j);
+ }
+ }
+ }
+ }
for (i = 0; i < node->as_nplans; i++)
{
PlanState *subnode = node->appendplans[i];
@@ -298,6 +576,13 @@ ExecReScanAppend(AppendState *node)
if (subnode->chgParam == NULL)
ExecReScan(subnode);
}
- node->as_whichplan = 0;
+
+ if (node->index)
+ {
+ node->as_whichplan = linitial_int(node->index);
+ node->as_whichpartition = 0;
+ }
+ else
+ node->as_whichplan = 0;
exec_append_initialize_next(node);
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c1a83ca..95dddab 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -224,6 +224,27 @@ _copyModifyTable(const ModifyTable *from)
return newnode;
}
+static PartScanKeyInfo *
+_copyPartScanKeyInfo(const PartScanKeyInfo *from)
+{
+ PartScanKeyInfo *newnode = makeNode(PartScanKeyInfo);
+
+ COPY_SCALAR_FIELD(n_eqkeys);
+ COPY_NODE_FIELD(eqkeys);
+
+ COPY_SCALAR_FIELD(n_minkeys);
+ COPY_NODE_FIELD(minkeys);
+ COPY_SCALAR_FIELD(min_incl);
+
+ COPY_SCALAR_FIELD(n_maxkeys);
+ COPY_NODE_FIELD(maxkeys);
+ COPY_SCALAR_FIELD(max_incl);
+
+ memcpy(newnode->keynullness, from->keynullness, sizeof(newnode->keynullness));
+
+ return newnode;
+}
+
/*
* _copyAppend
*/
@@ -242,6 +263,9 @@ _copyAppend(const Append *from)
*/
COPY_NODE_FIELD(partitioned_rels);
COPY_NODE_FIELD(appendplans);
+ COPY_NODE_FIELD(ex_keys);
+ COPY_NODE_FIELD(et_keys);
+ COPY_SCALAR_FIELD(parentoid);
return newnode;
}
@@ -5014,6 +5038,9 @@ copyObjectImpl(const void *from)
case T_PlaceHolderInfo:
retval = _copyPlaceHolderInfo(from);
break;
+ case T_PartScanKeyInfo:
+ retval = _copyPartScanKeyInfo(from);
+ break;
/*
* VALUE NODES
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ccb6a1f..15e6764 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2652,6 +2652,8 @@ parseNodeString(void)
return_value = _readLimit();
else if (MATCH("NESTLOOPPARAM", 13))
return_value = _readNestLoopParam();
+ else if (MATCH("PARTSCANKEYINFO", 15))
+ return_value = _readNestLoopParam();
else if (MATCH("PLANROWMARK", 11))
return_value = _readPlanRowMark();
else if (MATCH("PLANINVALITEM", 13))
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 12f409e..2d134c7 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -18,6 +18,7 @@
#include <limits.h>
#include <math.h>
+#include "catalog/partition.h"
#include "access/sysattr.h"
#include "access/tsmapi.h"
#include "catalog/partition.h"
@@ -139,11 +140,11 @@ static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels);
static List *get_append_rel_partitions(PlannerInfo *root,
RelOptInfo *rel,
- RangeTblEntry *rte);
+ RangeTblEntry *rte, bool is_runtime);
static List *match_clauses_to_partkey(RelOptInfo *rel,
List *clauses,
bool *contains_const,
- bool *constfalse);
+ bool *constfalse, bool is_runtime);
/*
@@ -865,6 +866,45 @@ intcmp(const void *va, const void *vb)
return (a > b) ? 1 : -1;
}
+PartScanKeyInfo*
+get_append_partition_info(RelOptInfo *rel, RangeTblEntry *rte,
+ bool is_runtime, List *clauses)
+{
+ bool constfalse,
+ contains_constant;
+ PartScanKeyInfo *keys = NULL;
+ List *partclauses;
+
+ /*
+ * Get the clauses that match the partition key, including information
+ * about any nullness tests against partition keys. Set keynullness to a
+ * invalid value of NullTestType, which 0 is not.
+ */
+ partclauses = match_clauses_to_partkey(rel,
+ list_copy(clauses),
+ &contains_constant,
+ &constfalse, is_runtime);
+
+ if (partclauses != NIL)
+ {
+ bool constfalse;
+ List *or_clauses;
+ Relation parent = heap_open(rte->relid, NoLock);
+ PartScanKeyInfo *temp_keys = makeNode(PartScanKeyInfo);
+ int nkeys = classify_partition_bounding_keys(parent, partclauses,
+ temp_keys,
+ &constfalse,
+ &or_clauses,
+ true);
+
+ if (nkeys > 0)
+ keys = temp_keys;
+
+ heap_close(parent, NoLock);
+ }
+ return keys;
+}
+
/*
* get_append_rel_partitions
* Return the list of partitions of rel that pass the clauses mentioned
@@ -875,7 +915,7 @@ intcmp(const void *va, const void *vb)
static List *
get_append_rel_partitions(PlannerInfo *root,
RelOptInfo *rel,
- RangeTblEntry *rte)
+ RangeTblEntry *rte, bool is_runtime)
{
Relation parent = heap_open(rte->relid, NoLock);
PartitionDesc partdesc = RelationGetPartitionDesc(parent);
@@ -898,7 +938,7 @@ get_append_rel_partitions(PlannerInfo *root,
partclauses = match_clauses_to_partkey(rel,
list_copy(rel->baserestrictinfo),
&contains_const,
- &constfalse);
+ &constfalse, is_runtime);
/*
* If the matched clauses contains at least some constant operands, use
@@ -1004,7 +1044,8 @@ static List *
match_clauses_to_partkey(RelOptInfo *rel,
List *clauses,
bool *contains_const,
- bool *constfalse)
+ bool *constfalse,
+ bool is_runtime)
{
PartitionScheme partscheme = rel->part_scheme;
List *result = NIL;
@@ -1051,7 +1092,8 @@ match_clauses_to_partkey(RelOptInfo *rel,
match_clauses_to_partkey(rel,
list_copy(((BoolExpr *) clause)->args),
&contains_const1,
- &constfalse1) != NIL)
+ &constfalse1,
+ is_runtime) != NIL)
{
result = lappend(result, clause);
*contains_const = contains_const1;
@@ -1123,6 +1165,12 @@ match_clauses_to_partkey(RelOptInfo *rel,
/* Neither argument matches the partition key. */
continue;
+ if (IsA(constexpr, Const) &&is_runtime)
+ continue;
+
+ if (IsA(constexpr, Param) &&!is_runtime)
+ continue;
+
/*
* Only allow strict operators to think sanely about the
* behavior with null arguments.
@@ -1228,7 +1276,7 @@ match_clauses_to_partkey(RelOptInfo *rel,
if (!PartCollMatchesExprColl(partcoll, saop_coll))
continue;
}
- else if (IsA(clause, NullTest))
+ else if (!is_runtime && IsA(clause, NullTest))
{
NullTest *nulltest = (NullTest *) clause;
Node *arg = (Node *) nulltest->arg;
@@ -1288,7 +1336,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
}
else
{
- rel_appinfos = get_append_rel_partitions(root, rel, rte);
+ rel_appinfos = get_append_rel_partitions(root, rel, rte, false);
rel->live_partitioned_rels = list_make1_int(rti);
}
@@ -1850,7 +1898,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
* if we have zero or one live subpath due to constraint exclusion.)
*/
if (subpaths_valid)
- add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0,
+ add_path(rel, (Path *) create_append_path(root, rel, subpaths, NULL, 0,
partitioned_rels));
/*
@@ -1877,7 +1925,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
Assert(parallel_workers > 0);
/* Generate a partial append path. */
- appendpath = create_append_path(rel, partial_subpaths, NULL,
+ appendpath = create_append_path(root, rel, partial_subpaths, NULL,
parallel_workers, partitioned_rels);
add_partial_path(rel, (Path *) appendpath);
}
@@ -1931,7 +1979,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
if (subpaths_valid)
add_path(rel, (Path *)
- create_append_path(rel, subpaths, required_outer, 0,
+ create_append_path(root, rel, subpaths, required_outer, 0,
partitioned_rels));
}
}
@@ -2168,7 +2216,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
rel->pathlist = NIL;
rel->partial_pathlist = NIL;
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
+ add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL));
/*
* We set the cheapest path immediately, to ensure that IS_DUMMY_REL()
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 7356683..af7e790 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1232,7 +1232,7 @@ mark_dummy_rel(RelOptInfo *rel)
rel->partial_pathlist = NIL;
/* Set up the dummy path */
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
+ add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL));
/* Set or update cheapest_total_path and related fields */
set_cheapest(rel);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9c74e39..9014ef2 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1001,6 +1001,47 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
return plan;
}
+static PartScanKeyInfo *
+replace_partition_nestloop_params(PlannerInfo *root, PartScanKeyInfo *old_keys)
+{
+ PartScanKeyInfo *keys;
+ ListCell *lc;
+
+ if (old_keys == NULL)
+ return NULL;
+
+ keys = makeNode(PartScanKeyInfo);
+ foreach(lc, old_keys->eqkeys)
+ {
+ Node *n = lfirst(lc);
+
+ keys->eqkeys = lappend(keys->eqkeys, replace_nestloop_params(root, n));
+ }
+ keys->n_eqkeys = old_keys->n_eqkeys;
+
+ foreach(lc, old_keys->minkeys)
+ {
+ Node *n = lfirst(lc);
+
+ keys->minkeys = lappend(keys->minkeys, replace_nestloop_params(root, n));
+ }
+ keys->n_minkeys = old_keys->n_minkeys;
+ keys->min_incl = old_keys->min_incl;
+
+ foreach(lc, old_keys->maxkeys)
+ {
+ Node *n = lfirst(lc);
+
+ keys->maxkeys = lappend(keys->maxkeys, replace_nestloop_params(root, n));
+ }
+ keys->n_maxkeys = old_keys->n_maxkeys;
+ keys->max_incl = old_keys->max_incl;
+
+ memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+ return keys;
+}
+
+
/*
* create_append_plan
* Create an Append plan for 'best_path' and (recursively) plans
@@ -1063,6 +1104,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
copy_generic_path_info(&plan->plan, (Path *) best_path);
+
+ plan->ex_keys = replace_partition_nestloop_params(root, best_path->ex_keys);
+ plan->et_keys = best_path->et_keys;
+ plan->parentoid = best_path->parentoid;
+
return (Plan *) plan;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd0e483..8a8876f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3675,7 +3675,7 @@ create_grouping_paths(PlannerInfo *root,
paths = lappend(paths, path);
}
path = (Path *)
- create_append_path(grouped_rel,
+ create_append_path(root, grouped_rel,
paths,
NULL,
0,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f620243..12d0f85 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -590,7 +590,7 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
+ path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
@@ -702,7 +702,7 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
/*
* Append the child results together.
*/
- path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
+ path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL);
/* We have to manually jam the right tlist into the path; ick */
path->pathtarget = create_pathtarget(root, tlist);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 30cdd3d..8634460 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5197,3 +5197,19 @@ tlist_matches_coltypelist(List *tlist, List *coltypelist)
return true;
}
+
+Node *
+eval_const_expressions_from_list(ParamListInfo prmList, Node *node)
+{
+ eval_const_expressions_context context;
+
+ if (prmList)
+ context.boundParams = prmList; /* bound Params */
+ else
+ context.boundParams = NULL;
+ context.root = NULL;
+ context.active_fns = NIL; /* nothing being recursively simplified */
+ context.case_val = NULL; /* no CASE being examined */
+ context.estimate = false; /* safe transformations only */
+ return eval_const_expressions_mutator(node, &context);
+}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 36ec025..8910d39 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1208,7 +1208,7 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
* Note that we must handle subpaths = NIL, representing a dummy access path.
*/
AppendPath *
-create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
+create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, Relids required_outer,
int parallel_workers, List *partitioned_rels)
{
AppendPath *pathnode = makeNode(AppendPath);
@@ -1253,6 +1253,40 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
}
+ if (root && (required_outer || rel->baserestrictinfo))
+ {
+ RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
+ List *query_quals = rel->baserestrictinfo;
+
+ if (rte && rte->rtekind == RTE_RELATION)
+ {
+ Oid poid = rte->relid;
+ Relation prel = relation_open(poid, NoLock);
+
+ if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ ParamPathInfo *ppi = pathnode->path.param_info;
+
+ /* Set the ex_keys for Exec Params */
+ if (ppi)
+ {
+ List *ppi_clauses = ppi->ppi_clauses;
+
+ pathnode->ex_keys =
+ get_append_partition_info(rel, rte, true, ppi_clauses);
+ }
+ /* Set et_keys for extern params */
+ if (query_quals)
+ {
+ pathnode->et_keys =
+ get_append_partition_info(rel, rte, true, query_quals);
+ }
+ pathnode->parentoid = poid;
+ }
+ relation_close(prel, NoLock);
+ }
+ }
+
return pathnode;
}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b06696b..ca83bb8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1556,6 +1556,9 @@ ParamPathInfo *
get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
{
ParamPathInfo *ppi;
+ Relids joinrelids;
+ List *pclauses;
+ ListCell *lc;
/* Unparameterized paths have no ParamPathInfo */
if (bms_is_empty(required_outer))
@@ -1567,11 +1570,29 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
if ((ppi = find_param_path_info(appendrel, required_outer)))
return ppi;
+
+ /*
+ * Generally for appendrel we don't fetch the clause from the the
+ * join clause (only we do so for baserel), but for identifying whether
+ * the appendrel is applicable for runtime pruning or not.
+ */
+ joinrelids = bms_union(appendrel->relids, required_outer);
+ pclauses = NIL;
+ foreach(lc, appendrel->joininfo)
+ {
+ RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+ if (join_clause_is_movable_into(rinfo,
+ appendrel->relids,
+ joinrelids))
+ pclauses = lappend(pclauses, rinfo);
+ }
+
/* Else build the ParamPathInfo */
ppi = makeNode(ParamPathInfo);
ppi->ppi_req_outer = required_outer;
ppi->ppi_rows = 0;
- ppi->ppi_clauses = NIL;
+ ppi->ppi_clauses = pclauses;
appendrel->ppilist = lappend(appendrel->ppilist, ppi);
return ppi;
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 7da99a9..eb54eaf 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -16,6 +16,7 @@
#include "fmgr.h"
#include "executor/tuptable.h"
#include "nodes/execnodes.h"
+#include "nodes/relation.h"
#include "parser/parse_node.h"
#include "utils/rel.h"
@@ -112,4 +113,11 @@ extern List *get_proposed_default_constraint(List *new_part_constaints);
void get_partitions_from_clauses(Relation relation, List *partclauses,
int *min_part_idx, int *max_part_idx,
Bitmapset **other_parts);
+
+ PartitionSet *get_partitions_for_keys(Relation rel,
+ PartScanKeyInfo *keys);
+
+extern int classify_partition_bounding_keys(Relation relation, List *clauses,
+ PartScanKeyInfo* keys, bool *constfalse,
+ List **or_clauses, bool is_runtime);
#endif /* PARTITION_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e05bc04..60cbb36 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1006,6 +1006,11 @@ typedef struct AppendState
PlanState **appendplans; /* array of PlanStates for my inputs */
int as_nplans;
int as_whichplan;
+ Oid parentoid;
+ List *index;
+
+ int as_whichpartition;
+ PartScanKeyInfo *ex_keys;
} AppendState;
/* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb49..5e09258 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -87,6 +87,7 @@ typedef enum NodeTag
T_NestLoopParam,
T_PlanRowMark,
T_PlanInvalItem,
+ T_PartScanKeyInfo,
/*
* TAGS FOR PLAN STATE NODES (execnodes.h)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index dd74efa..96f0632 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -19,6 +19,7 @@
#include "nodes/bitmapset.h"
#include "nodes/lockoptions.h"
#include "nodes/primnodes.h"
+#include "nodes/relation.h"
/* ----------------------------------------------------------------
@@ -248,6 +249,9 @@ typedef struct Append
/* RT indexes of non-leaf tables in a partition tree */
List *partitioned_rels;
List *appendplans;
+ Oid parentoid;
+ PartScanKeyInfo *ex_keys;
+ PartScanKeyInfo *et_keys;
} Append;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 9c67bd1..ee4f6de 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -363,6 +363,89 @@ typedef struct PartitionSchemeData
typedef struct PartitionSchemeData *PartitionScheme;
+/*
+ * PartScanKeyInfo
+ * Bounding scan keys to look up a table's partitions obtained from
+ * mutually-ANDed clauses containing partitioning-compatible operators
+ */
+typedef struct PartScanKeyInfo
+{
+ NodeTag type;
+
+ /*
+ * Expressions or constants constituting the *whole* partition key
+ * compared using partitioning-compatible equality operator(s). When
+ * n_eqkeys > 0, other keys (minkeys and maxkeys) are irrelevant.
+ */
+ List *eqkeys;
+ Datum eqkeys_datums[PARTITION_MAX_KEYS];
+ int n_eqkeys;
+
+ /*
+ * Expressions or constants that constitute the lower bound on the
+ * partition key or a prefix thereof. The last of those constants is
+ * compared using > or >= operator compatible with partitioning, making
+ * this the lower bound in a range query.
+ */
+ List *minkeys;
+ Datum minkeys_datums[PARTITION_MAX_KEYS];
+ int n_minkeys;
+ bool min_incl;
+
+ /*
+ * Expressions or constants that constitute the upper bound on the
+ * partition key or a prefix thereof. The last of those constants is
+ * compared using < or <= operator compatible with partitioning, making
+ * this the upper bound in a range query.
+ */
+ List *maxkeys;
+ Datum maxkeys_datums[PARTITION_MAX_KEYS];
+ int n_maxkeys;
+ bool max_incl;
+
+ /*
+ * Specifies the type of NullTest that was applied to each of the
+ * partition key columns or -1 if none was applied. Partitioning handles
+ * null partition keys specially depending on the partitioning method in
+ * use, so get_partitions_for_keys can return partitions according to the
+ * nullness condition for partition keys.
+ */
+ NullTestType keynullness[PARTITION_MAX_KEYS];
+} PartScanKeyInfo;
+
+ /* A data structure to represent a partition set. */
+typedef struct PartitionSet
+{
+ /*
+ * If either empty or all_parts is true, values of the other fields are
+ * invalid.
+ */
+ bool empty; /* contains no partitions */
+ bool all_parts; /* contains all partitions */
+
+ /*
+ * In the case of range partitioning, min_part_index contains the index of
+ * the lowest partition contained in the set and max_datum_index that of
+ * the highest partition (all partitions between these two indexes
+ * inclusive are part of the set.) Since other types of partitioning do
+ * not impose order on the data contained in successive partitions, these
+ * fields are not set in that case.
+ */
+ bool use_range;
+ int min_part_idx;
+ int max_part_idx;
+
+ /*
+ * other_parts contains the indexes of partitions that are not covered by
+ * the range defined by min/max indexes. For example, in the case of
+ * range partitoning, it will include default partition index (if any).
+ * Also, this is the only way to return list partitions, because list
+ * partitions do not have the same ordering property as range partitions,
+ * so it's pointless to use the min/max range method.
+ */
+ Bitmapset *other_parts;
+} PartitionSet;
+
/*----------
* RelOptInfo
* Per-relation information for planning/optimization
@@ -1289,6 +1372,9 @@ typedef struct AppendPath
/* RT indexes of non-leaf tables in a partition tree */
List *partitioned_rels;
List *subpaths; /* list of component Paths */
+ PartScanKeyInfo *ex_keys; /* Runtime Partition Pruning - EXEC Params */
+ PartScanKeyInfo *et_keys; /* Runtime Partition Pruning - EXTERN Params */
+ Oid parentoid;
} AppendPath;
#define IS_DUMMY_PATH(p) \
@@ -2334,4 +2420,5 @@ typedef struct JoinCostWorkspace
int numbatches;
} JoinCostWorkspace;
+
#endif /* RELATION_H */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index e367221..c19ee31 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -79,6 +79,7 @@ extern void CommuteOpExpr(OpExpr *clause);
extern void CommuteRowCompareExpr(RowCompareExpr *clause);
extern Node *eval_const_expressions(PlannerInfo *root, Node *node);
+extern Node *eval_const_expressions_from_list(ParamListInfo prm_list, Node *node);
extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c1f2fc9..d9b8dff 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -63,7 +63,7 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
List *bitmapquals);
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
List *tidquals, Relids required_outer);
-extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths,
+extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths,
Relids required_outer, int parallel_workers,
List *partitioned_rels);
extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ea886b6..e780b20 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -234,4 +234,8 @@ extern PathKey *make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
+extern PartScanKeyInfo *get_append_partition_info(
+ RelOptInfo *rel,
+ RangeTblEntry *rte, bool is_runtime, List *clauses);
+
#endif /* PATHS_H */
--
1.8.3.1