Skip to content

Commit 2cb9ec1

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 da9ee02 commit 2cb9ec1

File tree

1 file changed

+96
-20
lines changed

1 file changed

+96
-20
lines changed

src/backend/optimizer/plan/planner.c

+96-20
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,9 @@ inheritance_planner(PlannerInfo *root)
834834
{
835835
Query *parse = root->parse;
836836
int parentRTindex = parse->resultRelation;
837-
Bitmapset *resultRTindexes = NULL;
837+
Bitmapset *resultRTindexes;
838+
Bitmapset *subqueryRTindexes;
839+
Bitmapset *modifiableARIindexes;
838840
int nominalRelation = -1;
839841
List *final_rtable = NIL;
840842
int save_rel_array_size = 0;
@@ -845,6 +847,7 @@ inheritance_planner(PlannerInfo *root)
845847
List *returningLists = NIL;
846848
List *rowMarks;
847849
ListCell *lc;
850+
Index rti;
848851

849852
Assert(parse->commandType != CMD_INSERT);
850853

@@ -867,8 +870,10 @@ inheritance_planner(PlannerInfo *root)
867870
* subqueries during planning, and so we must create copies of them too,
868871
* except where they are target relations, which will each only be used in
869872
* a single plan.
873+
*
874+
* To begin with, we'll need a bitmapset of the target relation relids.
870875
*/
871-
resultRTindexes = bms_add_member(resultRTindexes, parentRTindex);
876+
resultRTindexes = bms_make_singleton(parentRTindex);
872877
foreach(lc, root->append_rel_list)
873878
{
874879
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
@@ -878,12 +883,57 @@ inheritance_planner(PlannerInfo *root)
878883
appinfo->child_relid);
879884
}
880885

886+
/*
887+
* Now, generate a bitmapset of the relids of the subquery RTEs, including
888+
* security-barrier RTEs that will become subqueries, as just explained.
889+
*/
890+
subqueryRTindexes = NULL;
891+
rti = 1;
892+
foreach(lc, parse->rtable)
893+
{
894+
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
895+
896+
if (rte->rtekind == RTE_SUBQUERY ||
897+
(rte->securityQuals != NIL &&
898+
!bms_is_member(rti, resultRTindexes)))
899+
subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
900+
rti++;
901+
}
902+
903+
/*
904+
* Next, we want to identify which AppendRelInfo items contain references
905+
* to any of the aforesaid subquery RTEs. These items will need to be
906+
* copied and modified to adjust their subquery references; whereas the
907+
* other ones need not be touched. It's worth being tense over this
908+
* because we can usually avoid processing most of the AppendRelInfo
909+
* items, thereby saving O(N^2) space and time when the target is a large
910+
* inheritance tree. We can identify AppendRelInfo items by their
911+
* child_relid, since that should be unique within the list.
912+
*/
913+
modifiableARIindexes = NULL;
914+
if (subqueryRTindexes != NULL)
915+
{
916+
foreach(lc, root->append_rel_list)
917+
{
918+
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
919+
920+
if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) ||
921+
bms_is_member(appinfo->child_relid, subqueryRTindexes) ||
922+
bms_overlap(pull_varnos((Node *) appinfo->translated_vars),
923+
subqueryRTindexes))
924+
modifiableARIindexes = bms_add_member(modifiableARIindexes,
925+
appinfo->child_relid);
926+
}
927+
}
928+
929+
/*
930+
* And now we can get on with generating a plan for each child table.
931+
*/
881932
foreach(lc, root->append_rel_list)
882933
{
883934
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
884935
PlannerInfo subroot;
885936
Plan *subplan;
886-
Index rti;
887937

888938
/* append_rel_list contains all append rels; ignore others */
889939
if (appinfo->parent_relid != parentRTindex)
@@ -917,9 +967,29 @@ inheritance_planner(PlannerInfo *root)
917967
/*
918968
* The append_rel_list likewise might contain references to subquery
919969
* RTEs (if any subqueries were flattenable UNION ALLs). So prepare
920-
* to apply ChangeVarNodes to that, too.
970+
* to apply ChangeVarNodes to that, too. As explained above, we only
971+
* want to copy items that actually contain such references; the rest
972+
* can just get linked into the subroot's append_rel_list.
973+
*
974+
* If we know there are no such references, we can just use the outer
975+
* append_rel_list unmodified.
921976
*/
922-
subroot.append_rel_list = (List *) copyObject(root->append_rel_list);
977+
if (modifiableARIindexes != NULL)
978+
{
979+
ListCell *lc2;
980+
981+
subroot.append_rel_list = NIL;
982+
foreach(lc2, root->append_rel_list)
983+
{
984+
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
985+
986+
if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
987+
appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
988+
989+
subroot.append_rel_list = lappend(subroot.append_rel_list,
990+
appinfo2);
991+
}
992+
}
923993

924994
/*
925995
* Add placeholders to the child Query's rangetable list to fill the
@@ -933,13 +1003,13 @@ inheritance_planner(PlannerInfo *root)
9331003

9341004
/*
9351005
* If this isn't the first child Query, generate duplicates of all
936-
* subquery RTEs, and adjust Var numbering to reference the
937-
* duplicates. To simplify the loop logic, we scan the original rtable
938-
* not the copy just made by adjust_appendrel_attrs; that should be OK
939-
* since subquery RTEs couldn't contain any references to the target
940-
* rel.
1006+
* subquery (or subquery-to-be) RTEs, and adjust Var numbering to
1007+
* reference the duplicates. To simplify the loop logic, we scan the
1008+
* original rtable not the copy just made by adjust_appendrel_attrs;
1009+
* that should be OK since subquery RTEs couldn't contain any
1010+
* references to the target rel.
9411011
*/
942-
if (final_rtable != NIL)
1012+
if (final_rtable != NIL && subqueryRTindexes != NULL)
9431013
{
9441014
ListCell *lr;
9451015

@@ -948,14 +1018,7 @@ inheritance_planner(PlannerInfo *root)
9481018
{
9491019
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
9501020

951-
/*
952-
* Copy subquery RTEs and RTEs with security barrier quals
953-
* that will be turned into subqueries, except those that are
954-
* target relations.
955-
*/
956-
if (rte->rtekind == RTE_SUBQUERY ||
957-
(rte->securityQuals != NIL &&
958-
!bms_is_member(rti, resultRTindexes)))
1021+
if (bms_is_member(rti, subqueryRTindexes))
9591022
{
9601023
Index newrti;
9611024

@@ -968,7 +1031,20 @@ inheritance_planner(PlannerInfo *root)
9681031
newrti = list_length(subroot.parse->rtable) + 1;
9691032
ChangeVarNodes((Node *) subroot.parse, rti, newrti, 0);
9701033
ChangeVarNodes((Node *) subroot.rowMarks, rti, newrti, 0);
971-
ChangeVarNodes((Node *) subroot.append_rel_list, rti, newrti, 0);
1034+
/* Skip processing unchanging parts of append_rel_list */
1035+
if (modifiableARIindexes != NULL)
1036+
{
1037+
ListCell *lc2;
1038+
1039+
foreach(lc2, subroot.append_rel_list)
1040+
{
1041+
AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
1042+
1043+
if (bms_is_member(appinfo2->child_relid,
1044+
modifiableARIindexes))
1045+
ChangeVarNodes((Node *) appinfo2, rti, newrti, 0);
1046+
}
1047+
}
9721048
rte = copyObject(rte);
9731049
ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);
9741050
subroot.parse->rtable = lappend(subroot.parse->rtable,

0 commit comments

Comments
 (0)