mirror of
https://github.com/HypoPG/hypopg
synced 2026-05-23 09:08:45 +00:00
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:
parent
951ea5fed2
commit
4aefb2de9b
5 changed files with 315 additions and 10 deletions
|
|
@ -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
|
||||
----------------------------------------------------------
|
||||
|
|
|
|||
39
hypopg.c
39
hypopg.c
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
187
hypopg_table.c
187
hypopg_table.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue