mirror of
https://github.com/postgres/postgres.git
synced 2026-04-22 06:37:06 -04:00
Store information about elided nodes in the final plan.
An extension (or core code) might want to reconstruct the planner's choice of join order from the final plan. To do so, it must be possible to find all of the RTIs that were part of the join problem in that plan. Commitadbad833f3, together with the earlier work in8c49a484e8, is enough to let us match up RTIs we see in the final plan with RTIs that we see during the planning cycle, but we still have a problem if the planner decides to drop some RTIs out of the final plan altogether. To fix that, when setrefs.c removes a SubqueryScan, single-child Append, or single-child MergeAppend from the final Plan tree, record the type of the removed node and the RTIs that the removed node would have scanned in the final plan tree. It would be natural to record this information on the child of the removed plan node, but that would require adding an additional pointer field to type Plan, which seems undesirable. So, instead, store the information in a separate list that the executor need never consult, and use the plan_node_id to identify the plan node with which the removed node is logically associated. Also, update pg_overexplain to display these details. Reviewed-by: Lukas Fittl <lukas@fittl.com> Reviewed-by: Jakub Wartak <jakub.wartak@enterprisedb.com> Reviewed-by: Greg Burd <greg@burd.me> Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com> Reviewed-by: Amit Langote <amitlangote09@gmail.com> Reviewed-by: Haibo Yan <tristan.yim@gmail.com> Reviewed-by: Alexandra Wang <alexandra.wang.oss@gmail.com> Discussion: http://postgr.es/m/CA+TgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4=ZHoKPzw@mail.gmail.com
This commit is contained in:
parent
adbad833f3
commit
0d4391b265
7 changed files with 127 additions and 5 deletions
|
|
@ -452,6 +452,8 @@ SELECT * FROM vegetables WHERE genus = 'daucus';
|
|||
Seq Scan on daucus vegetables
|
||||
Filter: (genus = 'daucus'::text)
|
||||
Scan RTI: 2
|
||||
Elided Node Type: Append
|
||||
Elided Node RTIs: 1
|
||||
RTI 1 (relation, inherited, in-from-clause):
|
||||
Eref: vegetables (id, name, genus)
|
||||
Relation: vegetables
|
||||
|
|
@ -465,7 +467,7 @@ SELECT * FROM vegetables WHERE genus = 'daucus';
|
|||
Relation Kind: relation
|
||||
Relation Lock Mode: AccessShareLock
|
||||
Unprunable RTIs: 1 2
|
||||
(16 rows)
|
||||
(18 rows)
|
||||
|
||||
-- Also test a case that involves a write.
|
||||
EXPLAIN (RANGE_TABLE, COSTS OFF)
|
||||
|
|
@ -499,6 +501,10 @@ SELECT * FROM vegetables v,
|
|||
-> Seq Scan on daucus vegetables
|
||||
Filter: (genus = 'daucus'::text)
|
||||
Scan RTI: 6
|
||||
Elided Node Type: Append
|
||||
Elided Node RTIs: 5
|
||||
Elided Node Type: SubqueryScan
|
||||
Elided Node RTIs: 2
|
||||
-> Append
|
||||
Append RTIs: 1
|
||||
-> Seq Scan on brassica v_1
|
||||
|
|
@ -542,7 +548,7 @@ SELECT * FROM vegetables v,
|
|||
Relation Kind: relation
|
||||
Relation Lock Mode: AccessShareLock
|
||||
Unprunable RTIs: 1 3 4 5 6
|
||||
(47 rows)
|
||||
(51 rows)
|
||||
|
||||
-- should show "Subplan: unnamed_subquery"
|
||||
EXPLAIN (RANGE_TABLE, COSTS OFF)
|
||||
|
|
@ -554,6 +560,10 @@ SELECT * FROM vegetables v,
|
|||
-> Seq Scan on daucus vegetables
|
||||
Filter: (genus = 'daucus'::text)
|
||||
Scan RTI: 6
|
||||
Elided Node Type: Append
|
||||
Elided Node RTIs: 5
|
||||
Elided Node Type: SubqueryScan
|
||||
Elided Node RTIs: 2
|
||||
-> Append
|
||||
Append RTIs: 1
|
||||
-> Seq Scan on brassica v_1
|
||||
|
|
@ -596,5 +606,5 @@ SELECT * FROM vegetables v,
|
|||
Relation Kind: relation
|
||||
Relation Lock Mode: AccessShareLock
|
||||
Unprunable RTIs: 1 3 4 5 6
|
||||
(46 rows)
|
||||
(50 rows)
|
||||
|
||||
|
|
|
|||
|
|
@ -191,6 +191,8 @@ overexplain_per_node_hook(PlanState *planstate, List *ancestors,
|
|||
*/
|
||||
if (options->range_table)
|
||||
{
|
||||
bool opened_elided_nodes = false;
|
||||
|
||||
switch (nodeTag(plan))
|
||||
{
|
||||
case T_SeqScan:
|
||||
|
|
@ -251,6 +253,43 @@ overexplain_per_node_hook(PlanState *planstate, List *ancestors,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
foreach_node(ElidedNode, n, es->pstmt->elidedNodes)
|
||||
{
|
||||
char *elidednodetag;
|
||||
|
||||
if (n->plan_node_id != plan->plan_node_id)
|
||||
continue;
|
||||
|
||||
if (!opened_elided_nodes)
|
||||
{
|
||||
ExplainOpenGroup("Elided Nodes", "Elided Nodes", false, es);
|
||||
opened_elided_nodes = true;
|
||||
}
|
||||
|
||||
switch (n->elided_type)
|
||||
{
|
||||
case T_Append:
|
||||
elidednodetag = "Append";
|
||||
break;
|
||||
case T_MergeAppend:
|
||||
elidednodetag = "MergeAppend";
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
elidednodetag = "SubqueryScan";
|
||||
break;
|
||||
default:
|
||||
elidednodetag = psprintf("%d", n->elided_type);
|
||||
break;
|
||||
}
|
||||
|
||||
ExplainOpenGroup("Elided Node", NULL, true, es);
|
||||
ExplainPropertyText("Elided Node Type", elidednodetag, es);
|
||||
overexplain_bitmapset("Elided Node RTIs", n->relids, es);
|
||||
ExplainCloseGroup("Elided Node", NULL, true, es);
|
||||
}
|
||||
if (opened_elided_nodes)
|
||||
ExplainCloseGroup("Elided Nodes", "Elided Nodes", false, es);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -666,6 +666,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
|
|||
result->paramExecTypes = glob->paramExecTypes;
|
||||
/* utilityStmt should be null, but we might as well copy it */
|
||||
result->utilityStmt = parse->utilityStmt;
|
||||
result->elidedNodes = glob->elidedNodes;
|
||||
result->stmt_location = parse->stmt_location;
|
||||
result->stmt_len = parse->stmt_len;
|
||||
|
||||
|
|
|
|||
|
|
@ -211,6 +211,9 @@ static List *set_windowagg_runcondition_references(PlannerInfo *root,
|
|||
List *runcondition,
|
||||
Plan *plan);
|
||||
|
||||
static void record_elided_node(PlannerGlobal *glob, int plan_node_id,
|
||||
NodeTag elided_type, Bitmapset *relids);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
|
@ -1460,10 +1463,17 @@ set_subqueryscan_references(PlannerInfo *root,
|
|||
|
||||
if (trivial_subqueryscan(plan))
|
||||
{
|
||||
Index scanrelid;
|
||||
|
||||
/*
|
||||
* We can omit the SubqueryScan node and just pull up the subplan.
|
||||
*/
|
||||
result = clean_up_removed_plan_level((Plan *) plan, plan->subplan);
|
||||
|
||||
/* Remember that we removed a SubqueryScan */
|
||||
scanrelid = plan->scan.scanrelid + rtoffset;
|
||||
record_elided_node(root->glob, plan->subplan->plan_node_id,
|
||||
T_SubqueryScan, bms_make_singleton(scanrelid));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1891,7 +1901,17 @@ set_append_references(PlannerInfo *root,
|
|||
Plan *p = (Plan *) linitial(aplan->appendplans);
|
||||
|
||||
if (p->parallel_aware == aplan->plan.parallel_aware)
|
||||
return clean_up_removed_plan_level((Plan *) aplan, p);
|
||||
{
|
||||
Plan *result;
|
||||
|
||||
result = clean_up_removed_plan_level((Plan *) aplan, p);
|
||||
|
||||
/* Remember that we removed an Append */
|
||||
record_elided_node(root->glob, p->plan_node_id, T_Append,
|
||||
offset_relid_set(aplan->apprelids, rtoffset));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1959,7 +1979,17 @@ set_mergeappend_references(PlannerInfo *root,
|
|||
Plan *p = (Plan *) linitial(mplan->mergeplans);
|
||||
|
||||
if (p->parallel_aware == mplan->plan.parallel_aware)
|
||||
return clean_up_removed_plan_level((Plan *) mplan, p);
|
||||
{
|
||||
Plan *result;
|
||||
|
||||
result = clean_up_removed_plan_level((Plan *) mplan, p);
|
||||
|
||||
/* Remember that we removed a MergeAppend */
|
||||
record_elided_node(root->glob, p->plan_node_id, T_MergeAppend,
|
||||
offset_relid_set(mplan->apprelids, rtoffset));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -3774,3 +3804,21 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
|
|||
return expression_tree_walker(node, extract_query_dependencies_walker,
|
||||
context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record some details about a node removed from the plan during setrefs
|
||||
* processing, for the benefit of code trying to reconstruct planner decisions
|
||||
* from examination of the final plan tree.
|
||||
*/
|
||||
static void
|
||||
record_elided_node(PlannerGlobal *glob, int plan_node_id,
|
||||
NodeTag elided_type, Bitmapset *relids)
|
||||
{
|
||||
ElidedNode *n = makeNode(ElidedNode);
|
||||
|
||||
n->plan_node_id = plan_node_id;
|
||||
n->elided_type = elided_type;
|
||||
n->relids = relids;
|
||||
|
||||
glob->elidedNodes = lappend(glob->elidedNodes, n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,9 @@ typedef struct PlannerGlobal
|
|||
/* type OIDs for PARAM_EXEC Params */
|
||||
List *paramExecTypes;
|
||||
|
||||
/* info about nodes elided from the plan during setrefs processing */
|
||||
List *elidedNodes;
|
||||
|
||||
/* highest PlaceHolderVar ID assigned */
|
||||
Index lastPHId;
|
||||
|
||||
|
|
|
|||
|
|
@ -152,6 +152,9 @@ typedef struct PlannedStmt
|
|||
/* non-null if this is utility stmt */
|
||||
Node *utilityStmt;
|
||||
|
||||
/* info about nodes elided from the plan during setrefs processing */
|
||||
List *elidedNodes;
|
||||
|
||||
/*
|
||||
* DefElem objects added by extensions, e.g. using planner_shutdown_hook
|
||||
*
|
||||
|
|
@ -1838,4 +1841,21 @@ typedef struct SubPlanRTInfo
|
|||
bool dummy;
|
||||
} SubPlanRTInfo;
|
||||
|
||||
/*
|
||||
* ElidedNode
|
||||
*
|
||||
* Information about nodes elided from the final plan tree: trivial subquery
|
||||
* scans, and single-child Append and MergeAppend nodes.
|
||||
*
|
||||
* plan_node_id is that of the surviving plan node, the sole child of the
|
||||
* one which was elided.
|
||||
*/
|
||||
typedef struct ElidedNode
|
||||
{
|
||||
NodeTag type;
|
||||
int plan_node_id;
|
||||
NodeTag elided_type;
|
||||
Bitmapset *relids;
|
||||
} ElidedNode;
|
||||
|
||||
#endif /* PLANNODES_H */
|
||||
|
|
|
|||
|
|
@ -710,6 +710,7 @@ EachState
|
|||
Edge
|
||||
EditableObjectType
|
||||
ElementsState
|
||||
ElidedNode
|
||||
EnableTimeoutParams
|
||||
EndDataPtrType
|
||||
EndDirectModify_function
|
||||
|
|
|
|||
Loading…
Reference in a new issue