On Thu, Jun 18, 2015 at 4:04 PM, Tom Lane <[email protected]> wrote: > ... although I see that range_table_mutator doesn't bother to copy/change > the column alias substructure. (Wonder if that gives rise to any > observable EXPLAIN bugs...) But it still seems like the append_rel_list > shouldn't be all that much bulkier than all the other crap that gets > generated inside this loop. We're not doing anything at all to reclaim > space consumed inside subquery_planner, and you'd think that would be > a lot. > > By the by, the tablesample additions to range_table_mutator are obviously > broken.
Whee. Meanwhile, here is an updated patch. The attached script (a modified version of something Thomas Munro sent me privately) contains a bunch of test queries. With the original patch I sent earlier, here are the timings I got: Q1 Time: 16215.887 ms Q2 Time: 18674.139 ms Q3 Time: 1029.093 ms Q4 Time: 86497.781 ms Q5 Time: 1143.851 ms This version is about the same for the last three, but the first two get much faster: Q1 Time: 2951.231 ms Q2 Time: 1251.809 ms Q3 Time: 1049.235 ms Q4 Time: 88477.803 ms Q5 Time: 1172.965 ms The speedup comes from the following trick: the first time we hit a query that might requite a ChangeVarNodes() on the append_rel_list, we compute a bitmapset of varnos that appear in that list. Then, every time we're thinking about doing a ChangeVarNodes from rti to new_rti, we check whether rti appears in the Bitmapset. If not, we can skip ChangeVarNodes(). That seems to reduce the amount of object-copying and object-walking attributable to this loop to something negligible in all of these test cases. The extraordinarily planning time for query 4 is caused by a completely different problem: SearchCatCache eats up huge amounts of CPU; its callers are get_attavgwidth and get_typlen. It's not clear to me why doubling the number of relations causes such an enormous explosion in calls to those functions; I would expect the number of calls to double, but presumably the actual increase is much more. That's a separate problem, though, unconnected to c03ad5602f529787968fa3201b35c119bbc6d782 and not necessarily a regression. -- Robert Haas EnterpriseDB: http://www.enterprisedb.com The Enterprise PostgreSQL Company
repro-planner-explosion.sh
Description: Bourne shell script
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8afde2b..6737818 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -43,6 +43,7 @@
#include "parser/parsetree.h"
#include "parser/parse_agg.h"
#include "rewrite/rewriteManip.h"
+#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
@@ -845,9 +846,19 @@ inheritance_planner(PlannerInfo *root)
List *returningLists = NIL;
List *rowMarks;
ListCell *lc;
+ MemoryContext myctx;
+ MemoryContext oldctx;
+ bool did_extract = false;
+ Relids append_rel_list_relids = NULL;
Assert(parse->commandType != CMD_INSERT);
+ myctx = AllocSetContextCreate(CurrentMemoryContext,
+ "inheritance_planner",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
/*
* We generate a modified instance of the original Query for each target
* relation, plan that, and put all the plans into a list that will be
@@ -908,18 +919,16 @@ inheritance_planner(PlannerInfo *root)
/*
* The rowMarks list might contain references to subquery RTEs, so
- * make a copy that we can apply ChangeVarNodes to. (Fortunately, the
- * executor doesn't need to see the modified copies --- we can just
- * pass it the original rowMarks list.)
+ * make a copy that we can apply ChangeVarNodes to. If any security
+ * barrier quals are present, the rowMarks list may be further modified
+ * by grouping_planner. (Fortunately, the executor doesn't need to see
+ * the modified copies --- we can just pass it the original rowMarks
+ * list. For the same reason, we can arrange to throw away the copy
+ * we make here relatively quickly.)
*/
+ oldctx = MemoryContextSwitchTo(myctx);
subroot.rowMarks = (List *) copyObject(root->rowMarks);
-
- /*
- * 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.
- */
- subroot.append_rel_list = (List *) copyObject(root->append_rel_list);
+ MemoryContextSwitchTo(oldctx);
/*
* Add placeholders to the child Query's rangetable list to fill the
@@ -942,6 +951,7 @@ inheritance_planner(PlannerInfo *root)
if (final_rtable != NIL)
{
ListCell *lr;
+ bool did_copy = false;
rti = 1;
foreach(lr, parse->rtable)
@@ -968,7 +978,39 @@ 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);
+
+ /*
+ * The append_rel_list likewise might contain references
+ * to subquery RTEs (if any subqueries were flattenable
+ * UNION ALLs). We can't modify the original copy owned
+ * by the parent root, so we have to copy the list if
+ * anything needs to be changed. But that may be expensive
+ * if there are many RTEs, so if we have not yet made a
+ * copy, do so only if ChangeVarNodes will not be a no-op.
+ * Even walking the whole node tree is expensive, so skip
+ * that unless a reference to this RTI is indeed present.
+ */
+ if (!did_extract)
+ {
+ append_rel_list_relids =
+ ExtractRTIndexes((Node *) subroot.append_rel_list,
+ 0);
+ did_extract = true;
+ }
+ if (bms_is_member(rti, append_rel_list_relids))
+ {
+ if (!did_copy)
+ {
+ oldctx = MemoryContextSwitchTo(myctx);
+ subroot.append_rel_list =
+ (List *) copyObject(root->append_rel_list);
+ MemoryContextSwitchTo(oldctx);
+ did_copy = true;
+ }
+ ChangeVarNodes((Node *) subroot.append_rel_list, rti,
+ newrti, 0);
+ }
+
rte = copyObject(rte);
ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);
subroot.parse->rtable = lappend(subroot.parse->rtable,
@@ -976,6 +1018,8 @@ inheritance_planner(PlannerInfo *root)
}
rti++;
}
+
+ bms_free(append_rel_list_relids);
}
/* There shouldn't be any OJ or LATERAL info to translate, as yet */
@@ -989,6 +1033,9 @@ inheritance_planner(PlannerInfo *root)
/* Generate plan */
subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ );
+ /* Reclaim some memory */
+ MemoryContextReset(myctx);
+
/*
* Planning may have modified the query result relation (if there were
* security barrier quals on the result RTE).
@@ -1097,6 +1144,8 @@ inheritance_planner(PlannerInfo *root)
Assert(!parse->onConflict);
}
+ MemoryContextDelete(myctx);
+
/* Mark result as unordered (probably unnecessary) */
root->query_pathkeys = NIL;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1da90ff..3075b9a 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -674,6 +674,152 @@ adjust_relid_set(Relids relids, int oldrelid, int newrelid)
}
/*
+ * ExtractRTIndexes - find all RT indexes referenced in a node tree
+ *
+ * This is similar to pull_varnos, but it needs to use exactly the same
+ * criteria as ChangeVarNodes, and pull_varnos does not.
+ */
+
+typedef struct
+{
+ int sublevels_up;
+ Relids varnos;
+} ExtractRTIndexes_context;
+
+static bool
+ExtractRTIndexes_walker(Node *node, ExtractRTIndexes_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup == context->sublevels_up)
+ context->varnos = bms_add_member(context->varnos, var->varno);
+ return false;
+ }
+ if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+ if (context->sublevels_up == 0)
+ context->varnos = bms_add_member(context->varnos, cexpr->cvarno);
+ return false;
+ }
+ if (IsA(node, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+
+ if (context->sublevels_up == 0)
+ context->varnos = bms_add_member(context->varnos, rtr->rtindex);
+ /* the subquery itself is visited separately */
+ return false;
+ }
+ if (IsA(node, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) node;
+
+ if (context->sublevels_up == 0)
+ context->varnos = bms_add_member(context->varnos, j->rtindex);
+ /* fall through to examine children */
+ }
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ if (phv->phlevelsup == context->sublevels_up)
+ context->varnos = bms_add_members(context->varnos, phv->phrels);
+ /* fall through to examine children */
+ }
+ if (IsA(node, PlanRowMark))
+ {
+ PlanRowMark *rowmark = (PlanRowMark *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ context->varnos = bms_add_member(context->varnos, rowmark->rti);
+ context->varnos = bms_add_member(context->varnos, rowmark->prti);
+ }
+ return false;
+ }
+ if (IsA(node, AppendRelInfo))
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ context->varnos = bms_add_member(context->varnos,
+ appinfo->parent_relid);
+ context->varnos = bms_add_member(context->varnos,
+ appinfo->child_relid);
+ }
+ /* fall through to examine children */
+ }
+ /* Shouldn't need to handle other planner auxiliary nodes here */
+ Assert(!IsA(node, SpecialJoinInfo));
+ Assert(!IsA(node, LateralJoinInfo));
+ Assert(!IsA(node, PlaceHolderInfo));
+ Assert(!IsA(node, MinMaxAggInfo));
+
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, ExtractRTIndexes_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, ExtractRTIndexes_walker,
+ (void *) context);
+}
+
+Relids
+ExtractRTIndexes(Node *node, int sublevels_up)
+{
+ ExtractRTIndexes_context context;
+
+ context.sublevels_up = sublevels_up;
+ context.varnos = NULL;
+
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, go straight to query_tree_walker to make sure that
+ * sublevels_up doesn't get incremented prematurely.
+ */
+ if (node && IsA(node, Query))
+ {
+ Query *qry = (Query *) node;
+
+ if (sublevels_up == 0)
+ {
+ ListCell *l;
+
+ context.varnos = bms_add_member(context.varnos,
+ qry->resultRelation);
+ if (qry->onConflict)
+ context.varnos = bms_add_member(context.varnos,
+ qry->onConflict->exclRelIndex);
+
+ foreach(l, qry->rowMarks)
+ {
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ context.varnos = bms_add_member(context.varnos, rc->rti);
+ }
+ }
+ query_tree_walker(qry, ExtractRTIndexes_walker, (void *) &context, 0);
+ }
+ else
+ ExtractRTIndexes_walker(node, &context);
+
+ return context.varnos;
+}
+
+/*
* IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
*
* Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h
index bb56fa0..1fc1587 100644
--- a/src/include/rewrite/rewriteManip.h
+++ b/src/include/rewrite/rewriteManip.h
@@ -15,6 +15,7 @@
#define REWRITEMANIP_H
#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
typedef struct replace_rte_variables_context replace_rte_variables_context;
@@ -42,6 +43,7 @@ typedef enum ReplaceVarsNoMatchOption
extern void OffsetVarNodes(Node *node, int offset, int sublevels_up);
extern void ChangeVarNodes(Node *node, int old_varno, int new_varno,
int sublevels_up);
+extern Relids ExtractRTIndexes(Node *node, int sublevels_up);
extern void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
int min_sublevels_up);
extern void IncrementVarSublevelsUp_rtable(List *rtable,
-- Sent via pgsql-hackers mailing list ([email protected]) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers
