]> granicus.if.org Git - postgresql/commitdiff
In the executor, use an array of pointers to access the rangetable.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Oct 2018 19:48:17 +0000 (15:48 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 Oct 2018 19:48:17 +0000 (15:48 -0400)
Instead of doing a lot of list_nth() accesses to es_range_table,
create a flattened pointer array during executor startup and index
into that to get at individual RangeTblEntrys.

This eliminates one source of O(N^2) behavior with lots of partitions.
(I'm not exactly convinced that it's the most important source, but
it's an easy one to fix.)

Amit Langote and David Rowley

Discussion: https://postgr.es/m/468c85d9-540e-66a2-1dde-fec2b741e688@lab.ntt.co.jp

contrib/postgres_fdw/postgres_fdw.c
src/backend/commands/copy.c
src/backend/commands/trigger.c
src/backend/executor/execExprInterp.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/nodeLockRows.c
src/backend/replication/logical/worker.c
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/include/parser/parsetree.h

index c02287a55cade1b2635a983ca78ed3cbf2e6225b..b91b7d27de90a5b8c2956ecabc395538f540d562 100644 (file)
@@ -1345,7 +1345,7 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags)
                rtindex = fsplan->scan.scanrelid;
        else
                rtindex = bms_next_member(fsplan->fs_relids, -1);
-       rte = rt_fetch(rtindex, estate->es_range_table);
+       rte = exec_rt_fetch(rtindex, estate);
        userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
        /* Get info about foreign table. */
@@ -1731,8 +1731,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate,
                                                                                FdwModifyPrivateRetrievedAttrs);
 
        /* Find RTE. */
-       rte = rt_fetch(resultRelInfo->ri_RangeTableIndex,
-                                  mtstate->ps.state->es_range_table);
+       rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex,
+                                               mtstate->ps.state);
 
        /* Construct an execution state. */
        fmstate = create_foreign_modify(mtstate->ps.state,
@@ -2036,7 +2036,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
         * correspond to this partition if it is one of the UPDATE subplan target
         * rels; in that case, we can just use the existing RTE as-is.
         */
-       rte = list_nth(estate->es_range_table, resultRelation - 1);
+       rte = exec_rt_fetch(resultRelation, estate);
        if (rte->relid != RelationGetRelid(rel))
        {
                rte = copyObject(rte);
@@ -2396,7 +2396,7 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
         * ExecCheckRTEPerms() does.
         */
        rtindex = estate->es_result_relation_info->ri_RangeTableIndex;
-       rte = rt_fetch(rtindex, estate->es_range_table);
+       rte = exec_rt_fetch(rtindex, estate);
        userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
 
        /* Get info about foreign table. */
@@ -5752,7 +5752,7 @@ conversion_error_callback(void *arg)
                        RangeTblEntry *rte;
                        Var                *var = (Var *) tle->expr;
 
-                       rte = rt_fetch(var->varno, estate->es_range_table);
+                       rte = exec_rt_fetch(var->varno, estate);
 
                        if (var->varattno == 0)
                                is_wholerow = true;
index df98e4ac626dea9d8786b2302ca0b590703385d8..86b0fb300ff8fb0931cd4958ee5f8cfed9fd4b98 100644 (file)
@@ -2484,9 +2484,8 @@ CopyFrom(CopyState cstate)
        estate->es_result_relations = resultRelInfo;
        estate->es_num_result_relations = 1;
        estate->es_result_relation_info = resultRelInfo;
-       estate->es_range_table = cstate->range_table;
-       estate->es_relations = (Relation *) palloc0(list_length(cstate->range_table) *
-                                                                                               sizeof(Relation));
+
+       ExecInitRangeTable(estate, cstate->range_table);
 
        /* Set up a tuple slot too */
        myslot = ExecInitExtraTupleSlot(estate, tupDesc);
index 136f9f0627549806ad86b6da53d4959c95ea5e45..240e85e39103f2b60dc638631c08f469acd0cce0 100644 (file)
@@ -75,7 +75,7 @@ static int    MyTriggerDepth = 0;
  * to be changed, however.
  */
 #define GetUpdatedColumns(relinfo, estate) \
-       (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
+       (exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols)
 
 /* Local function prototypes */
 static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
index c549e3db5d9db98828517eb6c0672680d274222b..1b0946b02f2c46ca39c57b8f8cc424cc6446083e 100644 (file)
@@ -3934,10 +3934,10 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
                 * perhaps other places.)
                 */
                if (econtext->ecxt_estate &&
-                       variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+                       variable->varno <= econtext->ecxt_estate->es_range_table_size)
                {
-                       RangeTblEntry *rte = rt_fetch(variable->varno,
-                                                                                 econtext->ecxt_estate->es_range_table);
+                       RangeTblEntry *rte = exec_rt_fetch(variable->varno,
+                                                                                          econtext->ecxt_estate);
 
                        if (rte->eref)
                                ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
index 8cbd75ed7aa36a3610ac6c44d78c69f12f9614f5..71b720eec9e0934a5fb9934a56fcbd4d3fdf7432 100644 (file)
@@ -109,9 +109,9 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
  * to be changed, however.
  */
 #define GetInsertedColumns(relinfo, estate) \
-       (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->insertedCols)
+       (exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->insertedCols)
 #define GetUpdatedColumns(relinfo, estate) \
-       (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
+       (exec_rt_fetch((relinfo)->ri_RangeTableIndex, estate)->updatedCols)
 
 /* end of local decls */
 
@@ -823,15 +823,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
        /*
         * initialize the node's execution state
         */
-       estate->es_range_table = rangeTable;
-
-       /*
-        * Allocate an array to store an open Relation corresponding to each
-        * rangeTable item, and initialize entries to NULL.  Relations are opened
-        * and stored here as needed.
-        */
-       estate->es_relations = (Relation *) palloc0(list_length(rangeTable) *
-                                                                                               sizeof(Relation));
+       ExecInitRangeTable(estate, rangeTable);
 
        estate->es_plannedstmt = plannedstmt;
 
@@ -918,9 +910,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
                                                                         resultRelIndex))
                                {
                                        Relation        resultRelDesc;
+                                       Oid                     reloid = exec_rt_fetch(resultRelIndex, estate)->relid;
 
-                                       resultRelDesc = heap_open(getrelid(resultRelIndex, rangeTable),
-                                                                                         NoLock);
+                                       resultRelDesc = heap_open(reloid, NoLock);
                                        Assert(CheckRelationLockedByMe(resultRelDesc, RowExclusiveLock, true));
                                        heap_close(resultRelDesc, NoLock);
                                }
@@ -962,7 +954,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
                        continue;
 
                /* get relation's OID (will produce InvalidOid if subquery) */
-               relid = getrelid(rc->rti, rangeTable);
+               relid = exec_rt_fetch(rc->rti, estate)->relid;
 
                switch (rc->markType)
                {
@@ -1619,8 +1611,8 @@ static void
 ExecEndPlan(PlanState *planstate, EState *estate)
 {
        ResultRelInfo *resultRelInfo;
-       int                     num_relations;
-       int                     i;
+       Index           num_relations;
+       Index           i;
        ListCell   *l;
 
        /*
@@ -1661,7 +1653,7 @@ ExecEndPlan(PlanState *planstate, EState *estate)
         * close whatever rangetable Relations have been opened.  We did not
         * acquire locks in ExecGetRangeTableRelation, so don't release 'em here.
         */
-       num_relations = list_length(estate->es_range_table);
+       num_relations = estate->es_range_table_size;
        for (i = 0; i < num_relations; i++)
        {
                if (estate->es_relations[i])
@@ -3087,7 +3079,7 @@ EvalPlanQualBegin(EPQState *epqstate, EState *parentestate)
                /*
                 * We already have a suitable child EPQ tree, so just reset it.
                 */
-               int                     rtsize = list_length(parentestate->es_range_table);
+               Index           rtsize = parentestate->es_range_table_size;
                PlanState  *planstate = epqstate->planstate;
 
                MemSet(estate->es_epqScanDone, 0, rtsize * sizeof(bool));
@@ -3136,11 +3128,11 @@ static void
 EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
 {
        EState     *estate;
-       int                     rtsize;
+       Index           rtsize;
        MemoryContext oldcontext;
        ListCell   *l;
 
-       rtsize = list_length(parentestate->es_range_table);
+       rtsize = parentestate->es_range_table_size;
 
        epqstate->estate = estate = CreateExecutorState();
 
@@ -3164,6 +3156,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
        estate->es_snapshot = parentestate->es_snapshot;
        estate->es_crosscheck_snapshot = parentestate->es_crosscheck_snapshot;
        estate->es_range_table = parentestate->es_range_table;
+       estate->es_range_table_array = parentestate->es_range_table_array;
+       estate->es_range_table_size = parentestate->es_range_table_size;
        estate->es_relations = parentestate->es_relations;
        estate->es_plannedstmt = parentestate->es_plannedstmt;
        estate->es_junkFilter = parentestate->es_junkFilter;
index a89ef2a482f93aca7db50b7075019fd0b4581e7e..b75062f74c4446050992de50f150fcc126d1d9fa 100644 (file)
@@ -26,6 +26,8 @@
  *
  *             ExecOpenScanRelation    Common code for scan node init routines.
  *
+ *             ExecInitRangeTable              Set up executor's range-table-related data.
+ *
  *             ExecGetRangeTableRelation               Fetch Relation for a rangetable entry.
  *
  *             executor_errposition    Report syntactic position of an error.
@@ -108,6 +110,8 @@ CreateExecutorState(void)
        estate->es_snapshot = InvalidSnapshot;  /* caller must initialize this */
        estate->es_crosscheck_snapshot = InvalidSnapshot;       /* no crosscheck */
        estate->es_range_table = NIL;
+       estate->es_range_table_array = NULL;
+       estate->es_range_table_size = 0;
        estate->es_relations = NULL;
        estate->es_plannedstmt = NULL;
 
@@ -670,6 +674,43 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags)
        return rel;
 }
 
+/*
+ * ExecInitRangeTable
+ *             Set up executor's range-table-related data
+ *
+ * We build an array from the range table list to allow faster lookup by RTI.
+ * (The es_range_table field is now somewhat redundant, but we keep it to
+ * avoid breaking external code unnecessarily.)
+ * This is also a convenient place to set up the parallel es_relations array.
+ */
+void
+ExecInitRangeTable(EState *estate, List *rangeTable)
+{
+       Index           rti;
+       ListCell   *lc;
+
+       /* Remember the range table List as-is */
+       estate->es_range_table = rangeTable;
+
+       /* Set up the equivalent array representation */
+       estate->es_range_table_size = list_length(rangeTable);
+       estate->es_range_table_array = (RangeTblEntry **)
+               palloc(estate->es_range_table_size * sizeof(RangeTblEntry *));
+       rti = 0;
+       foreach(lc, rangeTable)
+       {
+               estate->es_range_table_array[rti++] = lfirst_node(RangeTblEntry, lc);
+       }
+
+       /*
+        * Allocate an array to store an open Relation corresponding to each
+        * rangetable entry, and initialize entries to NULL.  Relations are opened
+        * and stored here as needed.
+        */
+       estate->es_relations = (Relation *)
+               palloc0(estate->es_range_table_size * sizeof(Relation));
+}
+
 /*
  * ExecGetRangeTableRelation
  *             Open the Relation for a range table entry, if not already done
@@ -681,13 +722,13 @@ ExecGetRangeTableRelation(EState *estate, Index rti)
 {
        Relation        rel;
 
-       Assert(rti > 0 && rti <= list_length(estate->es_range_table));
+       Assert(rti > 0 && rti <= estate->es_range_table_size);
 
        rel = estate->es_relations[rti - 1];
        if (rel == NULL)
        {
                /* First time through, so open the relation */
-               RangeTblEntry *rte = rt_fetch(rti, estate->es_range_table);
+               RangeTblEntry *rte = exec_rt_fetch(rti, estate);
 
                Assert(rte->rtekind == RTE_RELATION);
 
@@ -876,7 +917,7 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
                ListCell   *l;
                Index           rti = lfirst_int(lc);
                bool            is_result_rel = false;
-               Oid                     relid = getrelid(rti, estate->es_range_table);
+               Oid                     relid = exec_rt_fetch(rti, estate)->relid;
 
                /* If this is a result relation, already locked in InitPlan */
                foreach(l, stmt->nonleafResultRelations)
@@ -911,7 +952,7 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
                        else
                                lockmode = AccessShareLock;
 
-                       Assert(lockmode == rt_fetch(rti, estate->es_range_table)->rellockmode);
+                       Assert(lockmode == exec_rt_fetch(rti, estate)->rellockmode);
 
                        LockRelationOid(relid, lockmode);
                }
index 30de8a95ab8e2d63858932d75123e06bedcdeba5..6db345ae7acf160e4afb73252da38269cc273928 100644 (file)
@@ -400,7 +400,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
        /*
         * Create workspace in which we can remember per-RTE locked tuples
         */
-       lrstate->lr_ntables = list_length(estate->es_range_table);
+       lrstate->lr_ntables = estate->es_range_table_size;
        lrstate->lr_curtuples = (HeapTuple *)
                palloc0(lrstate->lr_ntables * sizeof(HeapTuple));
 
index c3443c5a2ce031715cde78552cc706838a4b0cdb..277da69fa6c1e3e6787ff65723533305b4aef52c 100644 (file)
@@ -200,8 +200,7 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
        rte->relid = RelationGetRelid(rel->localrel);
        rte->relkind = rel->localrel->rd_rel->relkind;
        rte->rellockmode = AccessShareLock;
-       estate->es_range_table = list_make1(rte);
-       estate->es_relations = (Relation *) palloc0(1 * sizeof(Relation));
+       ExecInitRangeTable(estate, list_make1(rte));
 
        resultRelInfo = makeNode(ResultRelInfo);
        InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0);
index 83ce989e3a34c08dc869fa08f25f4863612fe198..830f42dfe3c1fe41fa7f543439a8fab492d5cb96 100644 (file)
@@ -514,6 +514,15 @@ extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
 
 extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags);
 
+extern void ExecInitRangeTable(EState *estate, List *rangeTable);
+
+static inline RangeTblEntry *
+exec_rt_fetch(Index rti, EState *estate)
+{
+       Assert(rti > 0 && rti <= estate->es_range_table_size);
+       return estate->es_range_table_array[rti - 1];
+}
+
 extern Relation ExecGetRangeTableRelation(EState *estate, Index rti);
 
 extern int     executor_errposition(EState *estate, int location);
index 35646231a4c8207e562642676646e4f41124cda0..657b5936631f47f04b1378b465da1bb63fc4499f 100644 (file)
@@ -36,6 +36,7 @@ struct PlanState;                             /* forward references in this file */
 struct ParallelHashJoinState;
 struct ExprState;
 struct ExprContext;
+struct RangeTblEntry;                  /* avoid including parsenodes.h here */
 struct ExprEvalStep;                   /* avoid including execExpr.h everywhere */
 
 
@@ -486,7 +487,9 @@ typedef struct EState
        Snapshot        es_snapshot;    /* time qual to use */
        Snapshot        es_crosscheck_snapshot; /* crosscheck time qual for RI */
        List       *es_range_table; /* List of RangeTblEntry */
-       Relation   *es_relations;       /* Array of per-es_range_table-entry Relation
+       struct RangeTblEntry **es_range_table_array;    /* equivalent array */
+       Index           es_range_table_size;    /* size of the range table arrays */
+       Relation   *es_relations;       /* Array of per-range-table-entry Relation
                                                                 * pointers, or NULL if not yet opened */
        PlannedStmt *es_plannedstmt;    /* link to top of plan tree */
        const char *es_sourceText;      /* Source text from QueryDesc */
@@ -563,7 +566,7 @@ typedef struct EState
         * return, or NULL if nothing to return; es_epqTupleSet[] is true if a
         * particular array entry is valid; and es_epqScanDone[] is state to
         * remember if the tuple has been returned already.  Arrays are of size
-        * list_length(es_range_table) and are indexed by scan node scanrelid - 1.
+        * es_range_table_size and are indexed by scan node scanrelid - 1.
         */
        HeapTuple  *es_epqTuple;        /* array of EPQ substitute tuples */
        bool       *es_epqTupleSet; /* true if EPQ tuple is provided */
index dd9ae658ac773f5019945de42f03141fc24ed3cf..fe16d7d1fa432ca4cd4926ee47683acc7e7c13ae 100644 (file)
 #define rt_fetch(rangetable_index, rangetable) \
        ((RangeTblEntry *) list_nth(rangetable, (rangetable_index)-1))
 
-/*
- *             getrelid
- *
- *             Given the range index of a relation, return the corresponding
- *             relation OID.  Note that InvalidOid will be returned if the
- *             RTE is for a non-relation-type RTE.
- */
-#define getrelid(rangeindex,rangetable) \
-       (rt_fetch(rangeindex, rangetable)->relid)
-
 /*
  * Given an RTE and an attribute number, return the appropriate
  * variable name or alias for that attribute of that RTE.