1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

Improve inheritance_planner()'s performance for large inheritance sets.

Commit c03ad5602f introduced a planner
performance regression for UPDATE/DELETE on large inheritance sets.
It required copying the append_rel_list (which is of size proportional to
the number of inherited tables) once for each inherited table, thus
resulting in O(N^2) time and memory consumption.  While it's difficult to
avoid that in general, the extra work only has to be done for
append_rel_list entries that actually reference subquery RTEs, which
inheritance-set entries will not.  So we can buy back essentially all of
the loss in cases without subqueries in FROM; and even for those, the added
work is mainly proportional to the number of UNION ALL subqueries.

Back-patch to 9.2, like the previous commit.

Tom Lane and Dean Rasheed, per a complaint from Thomas Munro.
This commit is contained in:
Tom Lane
2015-06-22 18:53:27 -04:00
parent 926efeb042
commit e538e510e1

View File

@ -721,6 +721,8 @@ inheritance_planner(PlannerInfo *root)
{ {
Query *parse = root->parse; Query *parse = root->parse;
int parentRTindex = parse->resultRelation; int parentRTindex = parse->resultRelation;
Bitmapset *subqueryRTindexes;
Bitmapset *modifiableARIindexes;
List *final_rtable = NIL; List *final_rtable = NIL;
int save_rel_array_size = 0; int save_rel_array_size = 0;
RelOptInfo **save_rel_array = NULL; RelOptInfo **save_rel_array = NULL;
@ -729,6 +731,7 @@ inheritance_planner(PlannerInfo *root)
List *returningLists = NIL; List *returningLists = NIL;
List *rowMarks; List *rowMarks;
ListCell *lc; ListCell *lc;
Index rti;
/* /*
* We generate a modified instance of the original Query for each target * We generate a modified instance of the original Query for each target
@ -744,13 +747,54 @@ inheritance_planner(PlannerInfo *root)
* (1) would result in a rangetable of length O(N^2) for N targets, with * (1) would result in a rangetable of length O(N^2) for N targets, with
* at least O(N^3) work expended here; and (2) would greatly complicate * at least O(N^3) work expended here; and (2) would greatly complicate
* management of the rowMarks list. * management of the rowMarks list.
*
* To begin with, generate a bitmapset of the relids of the subquery RTEs.
*/
subqueryRTindexes = NULL;
rti = 1;
foreach(lc, parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
if (rte->rtekind == RTE_SUBQUERY)
subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
rti++;
}
/*
* Next, we want to identify which AppendRelInfo items contain references
* to any of the aforesaid subquery RTEs. These items will need to be
* copied and modified to adjust their subquery references; whereas the
* other ones need not be touched. It's worth being tense over this
* because we can usually avoid processing most of the AppendRelInfo
* items, thereby saving O(N^2) space and time when the target is a large
* inheritance tree. We can identify AppendRelInfo items by their
* child_relid, since that should be unique within the list.
*/
modifiableARIindexes = NULL;
if (subqueryRTindexes != NULL)
{
foreach(lc, root->append_rel_list)
{
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) ||
bms_is_member(appinfo->child_relid, subqueryRTindexes) ||
bms_overlap(pull_varnos((Node *) appinfo->translated_vars),
subqueryRTindexes))
modifiableARIindexes = bms_add_member(modifiableARIindexes,
appinfo->child_relid);
}
}
/*
* And now we can get on with generating a plan for each child table.
*/ */
foreach(lc, root->append_rel_list) foreach(lc, root->append_rel_list)
{ {
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc); AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
PlannerInfo subroot; PlannerInfo subroot;
Plan *subplan; Plan *subplan;
Index rti;
/* append_rel_list contains all append rels; ignore others */ /* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex) if (appinfo->parent_relid != parentRTindex)
@ -784,9 +828,29 @@ inheritance_planner(PlannerInfo *root)
/* /*
* The append_rel_list likewise might contain references to subquery * The append_rel_list likewise might contain references to subquery
* RTEs (if any subqueries were flattenable UNION ALLs). So prepare * RTEs (if any subqueries were flattenable UNION ALLs). So prepare
* to apply ChangeVarNodes to that, too. * to apply ChangeVarNodes to that, too. As explained above, we only
* want to copy items that actually contain such references; the rest
* can just get linked into the subroot's append_rel_list.
*
* If we know there are no such references, we can just use the outer
* append_rel_list unmodified.
*/ */
subroot.append_rel_list = (List *) copyObject(root->append_rel_list); if (modifiableARIindexes != NULL)
{
ListCell *lc2;
subroot.append_rel_list = NIL;
foreach(lc2, root->append_rel_list)
{
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
subroot.append_rel_list = lappend(subroot.append_rel_list,
appinfo2);
}
}
/* /*
* Add placeholders to the child Query's rangetable list to fill the * Add placeholders to the child Query's rangetable list to fill the
@ -806,7 +870,7 @@ inheritance_planner(PlannerInfo *root)
* since subquery RTEs couldn't contain any references to the target * since subquery RTEs couldn't contain any references to the target
* rel. * rel.
*/ */
if (final_rtable != NIL) if (final_rtable != NIL && subqueryRTindexes != NULL)
{ {
ListCell *lr; ListCell *lr;
@ -815,7 +879,7 @@ inheritance_planner(PlannerInfo *root)
{ {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr); RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
if (rte->rtekind == RTE_SUBQUERY) if (bms_is_member(rti, subqueryRTindexes))
{ {
Index newrti; Index newrti;
@ -828,7 +892,20 @@ inheritance_planner(PlannerInfo *root)
newrti = list_length(subroot.parse->rtable) + 1; newrti = list_length(subroot.parse->rtable) + 1;
ChangeVarNodes((Node *) subroot.parse, rti, newrti, 0); ChangeVarNodes((Node *) subroot.parse, rti, newrti, 0);
ChangeVarNodes((Node *) subroot.rowMarks, rti, newrti, 0); ChangeVarNodes((Node *) subroot.rowMarks, rti, newrti, 0);
ChangeVarNodes((Node *) subroot.append_rel_list, rti, newrti, 0); /* Skip processing unchanging parts of append_rel_list */
if (modifiableARIindexes != NULL)
{
ListCell *lc2;
foreach(lc2, subroot.append_rel_list)
{
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
if (bms_is_member(appinfo2->child_relid,
modifiableARIindexes))
ChangeVarNodes((Node *) appinfo2, rti, newrti, 0);
}
}
rte = copyObject(rte); rte = copyObject(rte);
subroot.parse->rtable = lappend(subroot.parse->rtable, subroot.parse->rtable = lappend(subroot.parse->rtable,
rte); rte);