Skip to content

Commit

Permalink
Disable runtime partition pruning on hypothetical partitioned table
Browse files Browse the repository at this point in the history
Unfortunately, the runtime partition pruning code in v11 is written in such a
way that it can't be made compatible with hypothetical partitioning.
  • Loading branch information
yuzupy authored and rjuju committed Jul 13, 2019
1 parent 951ea5f commit 4aefb2d
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 10 deletions.
73 changes: 68 additions & 5 deletions expected/hypo_table.out
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,40 @@ EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM hypo_part_range WHERE id = hypo_num
Filter: (id = hypo_number_one())
(9 rows)

-- 6B.4 InitPlan
-- 6B.4 CTE with partitioning underneath an union
EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s;
QUERY PLAN
--------------------------------------------------------
CTE Scan on s
CTE s
-> Append
-> Result
-> Append
-> Seq Scan on part_range_1_10000
Filter: (id = hypo_number_one())
-> Seq Scan on part_range_10000_20000
Filter: (id = hypo_number_one())
-> Seq Scan on part_range_20000_30000
Filter: (id = hypo_number_one())
(11 rows)

EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
QUERY PLAN
-----------------------------------------------------------------------------
CTE Scan on s
CTE s
-> Append
-> Result
-> Append
-> Seq Scan on hypo_part_range hypo_part_range_1_10000
Filter: (id = hypo_number_one())
-> Seq Scan on hypo_part_range hypo_part_range_10000_20000
Filter: (id = hypo_number_one())
-> Seq Scan on hypo_part_range hypo_part_range_20000_30000
Filter: (id = hypo_number_one())
(11 rows)

-- 6B.5 InitPlan
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s);
QUERY PLAN
----------------------------------------------------------
Expand Down Expand Up @@ -1479,9 +1512,9 @@ EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM hypo_part_range WHERE id =
Filter: (id = hypo_number_one())
(11 rows)

-- 6B.5 enable runtime partition pruning
-- 6B.6 enable runtime partition pruning
SET enable_partition_pruning to true;
-- 6B.6 simple case
-- 6B.7 simple case
EXPLAIN (COSTS OFF) SELECT * FROM part_range WHERE id = hypo_number_one();
QUERY PLAN
------------------------------------------
Expand All @@ -1503,7 +1536,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = hypo_number_one();
Filter: (id = hypo_number_one())
(7 rows)

-- 6B.7 CTE
-- 6B.8 CTE
EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s;
QUERY PLAN
--------------------------------------------------
Expand All @@ -1529,7 +1562,37 @@ EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM hypo_part_range WHERE id = hypo_num
Filter: (id = hypo_number_one())
(9 rows)

-- 6B.8 InitPlan
-- 6B.9 CTE with partitioning underneath an union
EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s;
QUERY PLAN
--------------------------------------------------------
CTE Scan on s
CTE s
-> Append
-> Result
-> Append
Subplans Removed: 2
-> Seq Scan on part_range_1_10000
Filter: (id = hypo_number_one())
(8 rows)

EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
QUERY PLAN
-----------------------------------------------------------------------------
CTE Scan on s
CTE s
-> Append
-> Result
-> Append
-> Seq Scan on hypo_part_range hypo_part_range_1_10000
Filter: (id = hypo_number_one())
-> Seq Scan on hypo_part_range hypo_part_range_10000_20000
Filter: (id = hypo_number_one())
-> Seq Scan on hypo_part_range hypo_part_range_20000_30000
Filter: (id = hypo_number_one())
(11 rows)

-- 6B.10 InitPlan
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s);
QUERY PLAN
----------------------------------------------------------
Expand Down
39 changes: 39 additions & 0 deletions hypopg.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ static void hypo_set_rel_pathlist_hook(PlannerInfo *root,
RangeTblEntry *rte);
static set_rel_pathlist_hook_type prev_set_rel_pathlist_hook = NULL;
#endif
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
static PlannedStmt *hypo_planner_hook(Query *parse, int cursorOptions,
ParamListInfo boundParams);
static planner_hook_type prev_planner_hook = NULL;
#endif

static bool hypo_query_walker(Node *node, hypoWalkerContext *context);
static void hypo_CacheRelCallback(Datum arg, Oid relid);
Expand All @@ -136,6 +141,10 @@ _PG_init(void)
#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000
prev_set_rel_pathlist_hook = set_rel_pathlist_hook;
set_rel_pathlist_hook = hypo_set_rel_pathlist_hook;
#endif
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
prev_planner_hook = planner_hook;
planner_hook = hypo_planner_hook;
#endif
isExplain = false;
hypoIndexes = NIL;
Expand Down Expand Up @@ -180,6 +189,9 @@ _PG_fini(void)
#if PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 110000
set_rel_pathlist_hook = prev_set_rel_pathlist_hook;
#endif
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
planner_hook = prev_planner_hook;
#endif
}

/*---------------------------------
Expand Down Expand Up @@ -725,6 +737,33 @@ hypo_set_rel_pathlist_hook(PlannerInfo *root,
}
#endif

#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
/*
* If we found partitioned tables, disable runtime partition pruning for pg11.
* This restriction will be removed for pg12+.
*/
static PlannedStmt *
hypo_planner_hook(Query *parse, int cursorOptions,
ParamListInfo boundParams)
{
PlannedStmt *result;
hypoPlanWalkerContext hypo_context;

if (prev_planner_hook)
result = prev_planner_hook(parse, cursorOptions, boundParams);
else
result = standard_planner(parse, cursorOptions, boundParams);

if (HYPO_ENABLED() && hypoTables)
{
hypo_context.rtable = result->rtable;
plannedstmt_plan_walker((Node *) result, hypo_plan_walker, hypo_context);
}

return result;
}
#endif

/*
* Reset all stored entries.
*/
Expand Down
187 changes: 187 additions & 0 deletions hypopg_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -2642,9 +2642,196 @@ hypo_markDummyIfExcluded(PlannerInfo *root, RelOptInfo *rel,
/*
* TODO: re-estimate parent size just like set_append_rel_size()
*/
}
#endif
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000

/*
* Plan tree walking support, that can be called on a PlannedStmt or any Plan
* node. It'll call the given walker function for each Plan found and recurse
* in all of them. This is designed in a similar way to
* expression_tree_walker, refer to it for more details about the support
* routine and the rule for stopping or continuing the tree walk.
*
* BE CAREFUL: Please not that at the difference to expression_tree_walker, the
* support routine SHOULD NEVER call this function, as it would result in an
* endless loop (that should be caught by the stack depth check).
*/
bool
plannedstmt_plan_walker(Node *node, bool (*walker)(), hypoPlanWalkerContext context)
{
ListCell *lc;

if (node == NULL)
return false;

/* Guard against stack overflow due to overly complex plan tree */
check_stack_depth();

/*
* First, always pass the node to the walker function to ensure that it'll
* see all nodes. Note that this function should never call
* plannedstmt_plan_walker on the same node again, as it would otherwise
* cause an infinite recursion.
*/
if (walker(node, context))
return true;

switch (nodeTag(node))
{
case T_PlannedStmt:
{
Plan *plan;
List *subplans;

/* plantree */
plan = ((PlannedStmt *) node)->planTree;

if (plannedstmt_plan_walker((Node *) plan, walker, context))
return true;

/* subplans */
subplans = ((PlannedStmt *) node)->subplans;

if (subplans)
{
foreach(lc, subplans)
{
Node *subplan = (Node *) lfirst(lc);

/* some subplan can be NULL */
if (!subplan)
continue;

if (plannedstmt_plan_walker(subplan, walker, context))
return true;
}
}
break;
}

case T_Append:
{
Assert(innerPlan(node) == NULL);
Assert(outerPlan(node) == NULL);

foreach(lc, ((Append *) node)->appendplans)
{
if (plannedstmt_plan_walker((Node *) lfirst(lc), walker,
context))
return true;
}
break;
}

case T_MergeAppend:
{
Assert(innerPlan(node) == NULL);
Assert(outerPlan(node) == NULL);

foreach(lc, ((MergeAppend *) node)->mergeplans)
{
if (plannedstmt_plan_walker((Node *) lfirst(lc), walker,
context))
return true;
}
break;
}

case T_ModifyTable:
{
Assert(innerPlan(node) == NULL);
Assert(outerPlan(node) == NULL);

foreach(lc, ((ModifyTable *) node)->plans)
{
if (plannedstmt_plan_walker((Node *) lfirst(lc), walker,
context))
return true;
}
break;
}

case T_SubqueryScan:
{
SubqueryScan *scan = (SubqueryScan *) node;

Assert(innerPlan(node) == NULL);
Assert(outerPlan(node) == NULL);

if (plannedstmt_plan_walker((Node *) scan->subplan, walker,
context))
return true;
break;
}

case T_BitmapAnd:
case T_BitmapOr:
{
/*
* plannedstmt_plan_walker will not recurse BitmapAnd path
* and BitmapOr path, because they can't contain Append nodes.
* The purpose of this function is finding Append nodes, so it
* will skip these unneeded nodes on a performance point of view
*/
break;
}

default:
{
if (innerPlan(node))
{
if (plannedstmt_plan_walker((Node *) innerPlan(node), walker,
context))
return true;
}

if (outerPlan(node))
{
if (plannedstmt_plan_walker((Node *) outerPlan(node), walker,
context))
return true;
}
break;
}
}
return false;
}

/*
* To disable runtime partition pruning, we search Append node from
* PlannedStmt using plannedstmt_plan_walker() and hypo_plan_walker().
* If we find Append node created by hypothetical partitions, we reset
* PartitionPruneInfo.
*/
bool
hypo_plan_walker(Node *node, hypoPlanWalkerContext context)
{
switch (nodeTag(node))
{
case T_Append:
{
ListCell *cell;
Index rt;
RangeTblEntry *rte;

/* check if this Append node was created by hypothetical partitions */
foreach(cell, ((Append *) node)->partitioned_rels)
{
rt = lfirst_int(cell);
rte = list_nth_node(RangeTblEntry, context.rtable, rt-1);
if (hypo_table_oid_is_hypothetical(rte->relid))
((Append *) node)->part_prune_info = NULL;
}
return false;
}

default:
return false;
}

}

#endif

#if PG_VERSION_NUM >= 110000
Expand Down
10 changes: 10 additions & 0 deletions include/hypopg_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ void hypo_injectHypotheticalPartitioning(PlannerInfo *root,
void hypo_markDummyIfExcluded(PlannerInfo *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
#endif
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000

typedef struct hypoPlanWalkerContext
{
List *rtable;
}hypoPlanWalkerContext;

bool plannedstmt_plan_walker(Node *node, bool (*walker)(), hypoPlanWalkerContext context);
bool hypo_plan_walker(Node *node, hypoPlanWalkerContext context);
#endif
#endif

#endif
Loading

0 comments on commit 4aefb2d

Please sign in to comment.