]> granicus.if.org Git - postgresql/commitdiff
Improve inheritance_planner()'s performance for large inheritance sets.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 22 Jun 2015 22:53:27 +0000 (18:53 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 22 Jun 2015 22:53:27 +0000 (18:53 -0400)
Commit c03ad5602f529787968fa3201b35c119bbc6d782 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.

src/backend/optimizer/plan/planner.c

index 8afde2b7d5069707e346901f819bed888a2333ee..a6ce96efc48623c187233a1b04e45a64f8eeeae0 100644 (file)
@@ -834,7 +834,9 @@ inheritance_planner(PlannerInfo *root)
 {
        Query      *parse = root->parse;
        int                     parentRTindex = parse->resultRelation;
-       Bitmapset  *resultRTindexes = NULL;
+       Bitmapset  *resultRTindexes;
+       Bitmapset  *subqueryRTindexes;
+       Bitmapset  *modifiableARIindexes;
        int                     nominalRelation = -1;
        List       *final_rtable = NIL;
        int                     save_rel_array_size = 0;
@@ -845,6 +847,7 @@ inheritance_planner(PlannerInfo *root)
        List       *returningLists = NIL;
        List       *rowMarks;
        ListCell   *lc;
+       Index           rti;
 
        Assert(parse->commandType != CMD_INSERT);
 
@@ -867,8 +870,10 @@ inheritance_planner(PlannerInfo *root)
         * subqueries during planning, and so we must create copies of them too,
         * except where they are target relations, which will each only be used in
         * a single plan.
+        *
+        * To begin with, we'll need a bitmapset of the target relation relids.
         */
-       resultRTindexes = bms_add_member(resultRTindexes, parentRTindex);
+       resultRTindexes = bms_make_singleton(parentRTindex);
        foreach(lc, root->append_rel_list)
        {
                AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
@@ -878,12 +883,57 @@ inheritance_planner(PlannerInfo *root)
                                                                                         appinfo->child_relid);
        }
 
+       /*
+        * Now, generate a bitmapset of the relids of the subquery RTEs, including
+        * security-barrier RTEs that will become subqueries, as just explained.
+        */
+       subqueryRTindexes = NULL;
+       rti = 1;
+       foreach(lc, parse->rtable)
+       {
+               RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+
+               if (rte->rtekind == RTE_SUBQUERY ||
+                       (rte->securityQuals != NIL &&
+                        !bms_is_member(rti, resultRTindexes)))
+                       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)
        {
                AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
                PlannerInfo subroot;
                Plan       *subplan;
-               Index           rti;
 
                /* append_rel_list contains all append rels; ignore others */
                if (appinfo->parent_relid != parentRTindex)
@@ -917,9 +967,29 @@ inheritance_planner(PlannerInfo *root)
                /*
                 * The append_rel_list likewise might contain references to subquery
                 * 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
@@ -933,13 +1003,13 @@ inheritance_planner(PlannerInfo *root)
 
                /*
                 * If this isn't the first child Query, generate duplicates of all
-                * subquery RTEs, and adjust Var numbering to reference the
-                * duplicates. To simplify the loop logic, we scan the original rtable
-                * not the copy just made by adjust_appendrel_attrs; that should be OK
-                * since subquery RTEs couldn't contain any references to the target
-                * rel.
+                * subquery (or subquery-to-be) RTEs, and adjust Var numbering to
+                * reference the duplicates.  To simplify the loop logic, we scan the
+                * original rtable not the copy just made by adjust_appendrel_attrs;
+                * that should be OK since subquery RTEs couldn't contain any
+                * references to the target rel.
                 */
-               if (final_rtable != NIL)
+               if (final_rtable != NIL && subqueryRTindexes != NULL)
                {
                        ListCell   *lr;
 
@@ -948,14 +1018,7 @@ inheritance_planner(PlannerInfo *root)
                        {
                                RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
 
-                               /*
-                                * Copy subquery RTEs and RTEs with security barrier quals
-                                * that will be turned into subqueries, except those that are
-                                * target relations.
-                                */
-                               if (rte->rtekind == RTE_SUBQUERY ||
-                                       (rte->securityQuals != NIL &&
-                                        !bms_is_member(rti, resultRTindexes)))
+                               if (bms_is_member(rti, subqueryRTindexes))
                                {
                                        Index           newrti;
 
@@ -968,7 +1031,20 @@ inheritance_planner(PlannerInfo *root)
                                        newrti = list_length(subroot.parse->rtable) + 1;
                                        ChangeVarNodes((Node *) subroot.parse, 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);
                                        ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);
                                        subroot.parse->rtable = lappend(subroot.parse->rtable,