I wrote: > BTW, further on the subject of performance --- I'm aware of at least > these topics for follow-on patches:
> * Fix places that are maintaining arrays parallel to Lists for > access-speed reasons (at least simple_rte_array, append_rel_array, > es_range_table_array). Attached is a patch that removes simple_rte_array in favor of just accessing the query's rtable directly. I concluded that there was not much point in messing with simple_rel_array or append_rel_array, because they are not in fact just mirrors of some List. There's no List at all of baserel RelOptInfos, and while we do have a list of AppendRelInfos, it's a compact, random-order list not one indexable by child relid. Having done this, though, I'm a bit discouraged about whether to commit it. In light testing, it's not any faster than HEAD and in complex queries seems to actually be a bit slower. I suspect the reason is that we've effectively replaced root->simple_rte_array[i] with root->parse->rtable->elements[i-1] and the two extra levels of indirection are taking their toll. It'd be possible to get rid of one of those indirections by maintaining a copy of root->parse->rtable directly in PlannerInfo; but that throws away most of the intellectual appeal of not having two sources of truth to maintain, and it won't completely close the performance gap. Other minor objections include: * Many RTE accesses now look randomly different from adjacent RelOptInfo accesses. * The inheritance-expansion code is a bit sloppy about how much it will expand these arrays, which means it's possible in corner cases for length(parse->rtable) to be less than root->simple_rel_array_size-1. This resulted in a crash in add_other_rels_to_query, which was assuming it could fetch a possibly-null RTE pointer from indexes up to simple_rel_array_size-1. While that wasn't hard to fix, I wonder whether any third-party code has similar assumptions. So on the whole, I'm inclined not to do this. There are some cosmetic bits of this patch that I do want to commit though: I found some out-of-date comments, and I realized that it's pretty dumb not to just merge setup_append_rel_array into setup_simple_rel_arrays. The division of labor there is inconsistent with the fact that there's no such division in expand_planner_arrays. I still have hopes for getting rid of es_range_table_array though, and will look at that tomorrow or so. regards, tom lane
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 06a2058..8bd1c47 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -2198,7 +2198,7 @@ postgresPlanDirectModify(PlannerInfo *root, } else foreignrel = root->simple_rel_array[resultRelation]; - rte = root->simple_rte_array[resultRelation]; + rte = planner_rt_fetch(resultRelation, root); fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; /* diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index e9ee32b..fe7f8b1 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -307,7 +307,7 @@ set_base_rel_sizes(PlannerInfo *root) if (rel->reloptkind != RELOPT_BASEREL) continue; - rte = root->simple_rte_array[rti]; + rte = planner_rt_fetch(rti, root); /* * If parallelism is allowable for this query in general, see whether @@ -349,7 +349,7 @@ set_base_rel_pathlists(PlannerInfo *root) if (rel->reloptkind != RELOPT_BASEREL) continue; - set_rel_pathlist(root, rel, rti, root->simple_rte_array[rti]); + set_rel_pathlist(root, rel, rti, planner_rt_fetch(rti, root)); } } @@ -1008,7 +1008,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, continue; childRTindex = appinfo->child_relid; - childRTE = root->simple_rte_array[childRTindex]; + childRTE = planner_rt_fetch(childRTindex, root); /* * The child rel's RelOptInfo was already created during @@ -1239,7 +1239,7 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Re-locate the child RTE and RelOptInfo */ childRTindex = appinfo->child_relid; - childRTE = root->simple_rte_array[childRTindex]; + childRTE = planner_rt_fetch(childRTindex, root); childrel = root->simple_rel_array[childRTindex]; /* @@ -3742,9 +3742,8 @@ print_relids(PlannerInfo *root, Relids relids) { if (!first) printf(" "); - if (x < root->simple_rel_array_size && - root->simple_rte_array[x]) - printf("%s", root->simple_rte_array[x]->eref->aliasname); + if (x <= list_length(root->parse->rtable)) + printf("%s", planner_rt_fetch(x, root)->eref->aliasname); else printf("%d", x); first = false; diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c index d19ff41..2576439 100644 --- a/src/backend/optimizer/plan/analyzejoins.c +++ b/src/backend/optimizer/plan/analyzejoins.c @@ -609,7 +609,7 @@ rel_supports_distinctness(PlannerInfo *root, RelOptInfo *rel) } else if (rel->rtekind == RTE_SUBQUERY) { - Query *subquery = root->simple_rte_array[rel->relid]->subquery; + Query *subquery = planner_rt_fetch(rel->relid, root)->subquery; /* Check if the subquery has any qualities that support distinctness */ if (query_supports_distinctness(subquery)) @@ -660,7 +660,7 @@ rel_is_distinct_for(PlannerInfo *root, RelOptInfo *rel, List *clause_list) else if (rel->rtekind == RTE_SUBQUERY) { Index relid = rel->relid; - Query *subquery = root->simple_rte_array[relid]->subquery; + Query *subquery = planner_rt_fetch(relid, root)->subquery; List *colnos = NIL; List *opids = NIL; ListCell *l; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f232569..5689330 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4472,7 +4472,7 @@ create_hashjoin_plan(PlannerInfo *root, Var *var = (Var *) node; RangeTblEntry *rte; - rte = root->simple_rte_array[var->varno]; + rte = planner_rt_fetch(var->varno, root); if (rte->rtekind == RTE_RELATION) { skewTable = rte->relid; diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 73da0c2..4a2aaa2 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -148,7 +148,7 @@ add_other_rels_to_query(PlannerInfo *root) for (rti = 1; rti < root->simple_rel_array_size; rti++) { RelOptInfo *rel = root->simple_rel_array[rti]; - RangeTblEntry *rte = root->simple_rte_array[rti]; + RangeTblEntry *rte; /* there may be empty slots corresponding to non-baserel RTEs */ if (rel == NULL) @@ -159,6 +159,7 @@ add_other_rels_to_query(PlannerInfo *root) continue; /* If it's marked as inheritable, look for children. */ + rte = planner_rt_fetch(rti, root); if (rte->inh) expand_inherited_rtentry(root, rel, rte, rti); } @@ -351,7 +352,7 @@ find_lateral_references(PlannerInfo *root) static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex) { - RangeTblEntry *rte = root->simple_rte_array[rtindex]; + RangeTblEntry *rte = planner_rt_fetch(rtindex, root); List *vars; List *newvars; Relids where_needed; @@ -1086,7 +1087,7 @@ process_security_barrier_quals(PlannerInfo *root, int rti, Relids qualscope, bool below_outer_join) { - RangeTblEntry *rte = root->simple_rte_array[rti]; + RangeTblEntry *rte = planner_rt_fetch(rti, root); Index security_level = 0; ListCell *lc; diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index df3f8c2..3398bde 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -79,9 +79,7 @@ query_planner(PlannerInfo *root, root->initial_rels = NIL; /* - * Make a flattened version of the rangetable for faster access (this is - * OK because the rangetable won't change any more), and set up an empty - * array for indexing base relations. + * Set up arrays for accessing base relations and AppendRelInfos. */ setup_simple_rel_arrays(root); @@ -99,7 +97,7 @@ query_planner(PlannerInfo *root, if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = root->simple_rte_array[varno]; + RangeTblEntry *rte = planner_rt_fetch(varno, root); Assert(rte != NULL); if (rte->rtekind == RTE_RESULT) @@ -157,12 +155,6 @@ query_planner(PlannerInfo *root, } /* - * Populate append_rel_array with each AppendRelInfo to allow direct - * lookups by child relid. - */ - setup_append_rel_array(root); - - /* * Construct RelOptInfo nodes for all base relations used in the query. * Appendrel member relations ("other rels") will be added later. * diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 0f918dd..7a38955 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1754,17 +1754,6 @@ inheritance_planner(PlannerInfo *root) root->simple_rel_array = save_rel_array; root->append_rel_array = save_append_rel_array; - /* Must reconstruct master's simple_rte_array, too */ - root->simple_rte_array = (RangeTblEntry **) - palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *)); - rti = 1; - foreach(lc, final_rtable) - { - RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); - - root->simple_rte_array[rti++] = rte; - } - /* Put back adjusted rowmarks, too */ root->rowMarks = final_rowmarks; } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 5a11c12..a05ed10 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -132,17 +132,11 @@ plan_set_operations(PlannerInfo *root) /* * We'll need to build RelOptInfos for each of the leaf subqueries, which * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index - * arrays for that. + * arrays for those, and for AppendRelInfos in case they're needed. */ setup_simple_rel_arrays(root); /* - * Populate append_rel_array with each AppendRelInfo to allow direct - * lookups by child relid. - */ - setup_append_rel_array(root); - - /* * Find the leftmost component Query. We need to use its column names for * all generated tlists (else SELECT INTO won't work right). */ @@ -150,7 +144,7 @@ plan_set_operations(PlannerInfo *root) while (node && IsA(node, SetOperationStmt)) node = ((SetOperationStmt *) node)->larg; Assert(node && IsA(node, RangeTblRef)); - leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex]; + leftmostRTE = planner_rt_fetch(((RangeTblRef *) node)->rtindex, root); leftmostQuery = leftmostRTE->subquery; Assert(leftmostQuery != NULL); @@ -225,7 +219,7 @@ recurse_set_operations(Node *setOp, PlannerInfo *root, if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; - RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex]; + RangeTblEntry *rte = planner_rt_fetch(rtr->rtindex, root); Query *subquery = rte->subquery; PlannerInfo *subroot; RelOptInfo *final_rel; diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 38bc61e..5ed7737 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -481,12 +481,10 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } /* - * Store the RTE and appinfo in the respective PlannerInfo arrays, which - * the caller must already have allocated space for. + * Store the appinfo in the associated PlannerInfo array, which the caller + * must already have allocated space for. */ Assert(childRTindex < root->simple_rel_array_size); - Assert(root->simple_rte_array[childRTindex] == NULL); - root->simple_rte_array[childRTindex] = childrte; Assert(root->append_rel_array[childRTindex] == NULL); root->append_rel_array[childRTindex] = appinfo; @@ -601,7 +599,7 @@ expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, /* find the child RTE, which should already exist */ Assert(childRTindex < root->simple_rel_array_size); - childrte = root->simple_rte_array[childRTindex]; + childrte = planner_rt_fetch(childRTindex, root); Assert(childrte != NULL); /* Build the child RelOptInfo. */ diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 98e9948..848ca1a 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -177,7 +177,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, * index while we hold lock on the parent rel, and no lock type used * for queries blocks any other kind of index operation. */ - lmode = root->simple_rte_array[varno]->rellockmode; + lmode = planner_rt_fetch(varno, root)->rellockmode; foreach(l, indexoidlist) { diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 37d228c..1f9f037 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -67,46 +67,26 @@ static void build_child_join_reltarget(PlannerInfo *root, /* * setup_simple_rel_arrays - * Prepare the arrays we use for quickly accessing base relations. + * Prepare the arrays we use for quickly accessing base relations + * and AppendRelInfos. */ void setup_simple_rel_arrays(PlannerInfo *root) { - Index rti; + /* Arrays are accessed using RT indexes (1..N) */ + int size = list_length(root->parse->rtable) + 1; ListCell *lc; - /* Arrays are accessed using RT indexes (1..N) */ - root->simple_rel_array_size = list_length(root->parse->rtable) + 1; + root->simple_rel_array_size = size; - /* simple_rel_array is initialized to all NULLs */ + /* + * simple_rel_array is initialized to all NULLs, since no RelOptInfos + * exist yet. It'll be filled by later calls to build_simple_rel(). + */ root->simple_rel_array = (RelOptInfo **) - palloc0(root->simple_rel_array_size * sizeof(RelOptInfo *)); - - /* simple_rte_array is an array equivalent of the rtable list */ - root->simple_rte_array = (RangeTblEntry **) - palloc0(root->simple_rel_array_size * sizeof(RangeTblEntry *)); - rti = 1; - foreach(lc, root->parse->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - root->simple_rte_array[rti++] = rte; - } -} - -/* - * setup_append_rel_array - * Populate the append_rel_array to allow direct lookups of - * AppendRelInfos by child relid. - * - * The array remains unallocated if there are no AppendRelInfos. - */ -void -setup_append_rel_array(PlannerInfo *root) -{ - ListCell *lc; - int size = list_length(root->parse->rtable) + 1; + palloc0(size * sizeof(RelOptInfo *)); + /* append_rel_array is not needed if there are no AppendRelInfos */ if (root->append_rel_list == NIL) { root->append_rel_array = NULL; @@ -116,6 +96,12 @@ setup_append_rel_array(PlannerInfo *root) root->append_rel_array = (AppendRelInfo **) palloc0(size * sizeof(AppendRelInfo *)); + /* + * append_rel_array is filled with any already-existing AppendRelInfos, + * which currently could only come from UNION ALL flattening. We might + * add more later during inheritance expansion, but it's the + * responsibility of the expansion code to update the array properly. + */ foreach(lc, root->append_rel_list) { AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc); @@ -133,8 +119,12 @@ setup_append_rel_array(PlannerInfo *root) /* * expand_planner_arrays - * Expand the PlannerInfo's per-RTE arrays by add_size members + * Expand the PlannerInfo's per-baserel arrays by add_size members * and initialize the newly added entries to NULLs + * + * Note: this causes the append_rel_array to become allocated even if + * it was not before. This is okay for current uses, because we only call + * this when adding child relations, which always have AppendRelInfos. */ void expand_planner_arrays(PlannerInfo *root, int add_size) @@ -145,12 +135,6 @@ expand_planner_arrays(PlannerInfo *root, int add_size) new_size = root->simple_rel_array_size + add_size; - root->simple_rte_array = (RangeTblEntry **) - repalloc(root->simple_rte_array, - sizeof(RangeTblEntry *) * new_size); - MemSet(root->simple_rte_array + root->simple_rel_array_size, - 0, sizeof(RangeTblEntry *) * add_size); - root->simple_rel_array = (RelOptInfo **) repalloc(root->simple_rel_array, sizeof(RelOptInfo *) * new_size); @@ -190,7 +174,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) elog(ERROR, "rel %d already exists", relid); /* Fetch RTE for relation */ - rte = root->simple_rte_array[relid]; + rte = planner_rt_fetch(relid, root); Assert(rte != NULL); rel = makeNode(RelOptInfo); diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 23c74f7..05c18a4 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -793,7 +793,7 @@ statext_is_compatible_clause_internal(PlannerInfo *root, Node *clause, /* (Var op Const) or (Const op Var) */ if (is_opclause(clause)) { - RangeTblEntry *rte = root->simple_rte_array[relid]; + RangeTblEntry *rte = planner_rt_fetch(relid, root); OpExpr *expr = (OpExpr *) clause; Var *var; @@ -922,7 +922,7 @@ static bool statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums) { - RangeTblEntry *rte = root->simple_rte_array[relid]; + RangeTblEntry *rte = planner_rt_fetch(relid, root); RestrictInfo *rinfo = (RestrictInfo *) clause; Oid userid; diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 7eba59e..1c18499 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -4648,7 +4648,7 @@ static void examine_simple_variable(PlannerInfo *root, Var *var, VariableStatData *vardata) { - RangeTblEntry *rte = root->simple_rte_array[var->varno]; + RangeTblEntry *rte = planner_rt_fetch(var->varno, root); Assert(IsA(rte, RangeTblEntry)); @@ -5130,7 +5130,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, if (rel == NULL || rel->indexlist == NIL) return false; /* If it has indexes it must be a plain relation */ - rte = root->simple_rte_array[rel->relid]; + rte = planner_rt_fetch(rel->relid, root); Assert(rte->rtekind == RTE_RELATION); /* Search through the indexes to see if any match our problem */ diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index e3c579e..6f3115a 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -203,15 +203,7 @@ struct PlannerInfo int simple_rel_array_size; /* allocated size of array */ /* - * simple_rte_array is the same length as simple_rel_array and holds - * pointers to the associated rangetable entries. This lets us avoid - * rt_fetch(), which can be a bit slow once large inheritance sets have - * been expanded. - */ - RangeTblEntry **simple_rte_array; /* rangetable as an array */ - - /* - * append_rel_array is the same length as the above arrays, and holds + * append_rel_array is the same length as simple_rel_array, and holds * pointers to the corresponding AppendRelInfo entry indexed by * child_relid, or NULL if none. The array itself is not allocated if * append_rel_list is empty. @@ -365,14 +357,9 @@ struct PlannerInfo }; -/* - * In places where it's known that simple_rte_array[] must have been prepared - * already, we just index into it to fetch RTEs. In code that might be - * executed before or after entering query_planner(), use this macro. - */ +/* Handy macro for getting the RTE with rangetable index rti */ #define planner_rt_fetch(rti, root) \ - ((root)->simple_rte_array ? (root)->simple_rte_array[rti] : \ - rt_fetch(rti, (root)->parse->rtable)) + ((RangeTblEntry *) list_nth((root)->parse->rtable, (rti)-1)) /* * If multiple relations are partitioned the same way, all such partitions diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 182ffee..a12af54 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -277,7 +277,6 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path, * prototypes for relnode.c */ extern void setup_simple_rel_arrays(PlannerInfo *root); -extern void setup_append_rel_array(PlannerInfo *root); extern void expand_planner_arrays(PlannerInfo *root, int add_size); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent);