Disable runtime partition pruning on hypothetical partitioned table

Unfortunately, the runtime partition pruning code in v11 is written in such a
way that it can't be made compatible with hypothetical partitioning.
This commit is contained in:
Yuzuko Hosoya 2019-07-11 00:43:43 +09:00 committed by Julien Rouhaud
parent 951ea5fed2
commit 4aefb2de9b
5 changed files with 315 additions and 10 deletions

View file

@ -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
----------------------------------------------------------
@ -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
------------------------------------------
@ -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
--------------------------------------------------
@ -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
----------------------------------------------------------

View file

@ -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);
@ -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;
@ -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
}
/*---------------------------------
@ -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.
*/

View file

@ -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

View file

@ -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

View file

@ -250,18 +250,24 @@ EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = hypo_number_one();
-- 6B.3 CTE
EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s;
EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
-- 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;
EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
-- 6B.5 InitPlan
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s);
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s);
-- 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();
EXPLAIN (COSTS OFF) SELECT * FROM hypo_part_range WHERE id = hypo_number_one();
-- 6B.7 CTE
-- 6B.8 CTE
EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s;
EXPLAIN (COSTS OFF) WITH s AS (SELECT * FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
-- 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;
EXPLAIN (COSTS OFF) WITH s AS (SELECT 1 UNION ALL SELECT 2 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s;
-- 6B.10 InitPlan
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM part_range WHERE id = hypo_number_one()) SELECT * FROM s);
EXPLAIN (COSTS OFF) SELECT (WITH s AS (SELECT 1 FROM hypo_part_range WHERE id = hypo_number_one()) SELECT * FROM s);