Skip to content

Commit d8f9ab7

Browse files
committed
Improve inheritance_planner()'s performance for large inheritance sets.
Commit c03ad56 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.
1 parent d1c1e48 commit d8f9ab7

File tree

1 file changed

+83
-6
lines changed

1 file changed

+83
-6
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,8 @@ inheritance_planner(PlannerInfo *root)
787787
{
788788
Query *parse = root->parse;
789789
int parentRTindex = parse->resultRelation;
790+
Bitmapset *subqueryRTindexes;
791+
Bitmapset *modifiableARIindexes;
790792
List *final_rtable = NIL;
791793
int save_rel_array_size = 0;
792794
RelOptInfo **save_rel_array = NULL;
@@ -796,6 +798,7 @@ inheritance_planner(PlannerInfo *root)
796798
List *returningLists = NIL;
797799
List *rowMarks;
798800
ListCell *lc;
801+
Index rti;
799802

800803
/*
801804
* We generate a modified instance of the original Query for each target
@@ -811,13 +814,54 @@ inheritance_planner(PlannerInfo *root)
811814
* (1) would result in a rangetable of length O(N^2) for N targets, with
812815
* at least O(N^3) work expended here; and (2) would greatly complicate
813816
* management of the rowMarks list.
817+
*
818+
* To begin with, generate a bitmapset of the relids of the subquery RTEs.
819+
*/
820+
subqueryRTindexes = NULL;
821+
rti = 1;
822+
foreach(lc, parse->rtable)
823+
{
824+
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
825+
826+
if (rte->rtekind == RTE_SUBQUERY)
827+
subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
828+
rti++;
829+
}
830+
831+
/*
832+
* Next, we want to identify which AppendRelInfo items contain references
833+
* to any of the aforesaid subquery RTEs. These items will need to be
834+
* copied and modified to adjust their subquery references; whereas the
835+
* other ones need not be touched. It's worth being tense over this
836+
* because we can usually avoid processing most of the AppendRelInfo
837+
* items, thereby saving O(N^2) space and time when the target is a large
838+
* inheritance tree. We can identify AppendRelInfo items by their
839+
* child_relid, since that should be unique within the list.
840+
*/
841+
modifiableARIindexes = NULL;
842+
if (subqueryRTindexes != NULL)
843+
{
844+
foreach(lc, root->append_rel_list)
845+
{
846+
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
847+
848+
if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) ||
849+
bms_is_member(appinfo->child_relid, subqueryRTindexes) ||
850+
bms_overlap(pull_varnos((Node *) appinfo->translated_vars),
851+
subqueryRTindexes))
852+
modifiableARIindexes = bms_add_member(modifiableARIindexes,
853+
appinfo->child_relid);
854+
}
855+
}
856+
857+
/*
858+
* And now we can get on with generating a plan for each child table.
814859
*/
815860
foreach(lc, root->append_rel_list)
816861
{
817862
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
818863
PlannerInfo subroot;
819864
Plan *subplan;
820-
Index rti;
821865

822866
/* append_rel_list contains all append rels; ignore others */
823867
if (appinfo->parent_relid != parentRTindex)
@@ -851,9 +895,29 @@ inheritance_planner(PlannerInfo *root)
851895
/*
852896
* The append_rel_list likewise might contain references to subquery
853897
* RTEs (if any subqueries were flattenable UNION ALLs). So prepare
854-
* to apply ChangeVarNodes to that, too.
898+
* to apply ChangeVarNodes to that, too. As explained above, we only
899+
* want to copy items that actually contain such references; the rest
900+
* can just get linked into the subroot's append_rel_list.
901+
*
902+
* If we know there are no such references, we can just use the outer
903+
* append_rel_list unmodified.
855904
*/
856-
subroot.append_rel_list = (List *) copyObject(root->append_rel_list);
905+
if (modifiableARIindexes != NULL)
906+
{
907+
ListCell *lc2;
908+
909+
subroot.append_rel_list = NIL;
910+
foreach(lc2, root->append_rel_list)
911+
{
912+
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
913+
914+
if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
915+
appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
916+
917+
subroot.append_rel_list = lappend(subroot.append_rel_list,
918+
appinfo2);
919+
}
920+
}
857921

858922
/*
859923
* Add placeholders to the child Query's rangetable list to fill the
@@ -873,7 +937,7 @@ inheritance_planner(PlannerInfo *root)
873937
* since subquery RTEs couldn't contain any references to the target
874938
* rel.
875939
*/
876-
if (final_rtable != NIL)
940+
if (final_rtable != NIL && subqueryRTindexes != NULL)
877941
{
878942
ListCell *lr;
879943

@@ -882,7 +946,7 @@ inheritance_planner(PlannerInfo *root)
882946
{
883947
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
884948

885-
if (rte->rtekind == RTE_SUBQUERY)
949+
if (bms_is_member(rti, subqueryRTindexes))
886950
{
887951
Index newrti;
888952

@@ -895,7 +959,20 @@ inheritance_planner(PlannerInfo *root)
895959
newrti = list_length(subroot.parse->rtable) + 1;
896960
ChangeVarNodes((Node *) subroot.parse, rti, newrti, 0);
897961
ChangeVarNodes((Node *) subroot.rowMarks, rti, newrti, 0);
898-
ChangeVarNodes((Node *) subroot.append_rel_list, rti, newrti, 0);
962+
/* Skip processing unchanging parts of append_rel_list */
963+
if (modifiableARIindexes != NULL)
964+
{
965+
ListCell *lc2;
966+
967+
foreach(lc2, subroot.append_rel_list)
968+
{
969+
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
970+
971+
if (bms_is_member(appinfo2->child_relid,
972+
modifiableARIindexes))
973+
ChangeVarNodes((Node *) appinfo2, rti, newrti, 0);
974+
}
975+
}
899976
rte = copyObject(rte);
900977
subroot.parse->rtable = lappend(subroot.parse->rtable,
901978
rte);

0 commit comments

Comments
 (0)