From 944e715ff2aac4dcb1331679aea4dbfaf18c4b95 Mon Sep 17 00:00:00 2001 From: "dgrowley@gmail.com" Date: Thu, 27 Sep 2018 16:14:41 +1200 Subject: [PATCH v9 4/4] Revise executor range table relation opening/closing All requests to open range table relations in the executor now go through ExecRangeTableRelation(), which consults an array of Relation pointers indexed by RT index, an arrangement which allows the executor to have to heap_open any given range table relation only once. To speed up retrieving range table entries at arbitrary points within the executor, this introduceds an array of RangeTblEntry pointers in EState. InitPlan builds it from the es_range_table list. This also revises PartitionedRelPruneInfo node to contain the partitioned table's RT index instead of OID. With that change, ExecCreatePartitionPruneState can use ExecRangeTableRelation described above. Accessed Relations are now also closed during ExecEndPlan(). Callers of ExecRangeTableRelation() have no need to consider how their Relation objects are cleaned up. Authors: Amit Langote, David Rowley --- contrib/postgres_fdw/postgres_fdw.c | 16 +++--- src/backend/commands/copy.c | 3 +- src/backend/commands/trigger.c | 3 +- src/backend/executor/README | 4 +- src/backend/executor/execExprInterp.c | 7 +-- src/backend/executor/execMain.c | 81 +++++++++-------------------- src/backend/executor/execPartition.c | 39 +++----------- src/backend/executor/execUtils.c | 68 +++++++++++++++--------- src/backend/executor/nodeAppend.c | 6 --- src/backend/executor/nodeBitmapHeapscan.c | 7 --- src/backend/executor/nodeCustom.c | 4 -- src/backend/executor/nodeForeignscan.c | 4 -- src/backend/executor/nodeIndexonlyscan.c | 7 --- src/backend/executor/nodeIndexscan.c | 7 --- src/backend/executor/nodeLockRows.c | 2 +- src/backend/executor/nodeMergeAppend.c | 6 --- src/backend/executor/nodeModifyTable.c | 26 +++++---- src/backend/executor/nodeSamplescan.c | 5 -- src/backend/executor/nodeSeqscan.c | 7 --- src/backend/executor/nodeTidscan.c | 5 -- src/backend/nodes/copyfuncs.c | 2 +- src/backend/nodes/outfuncs.c | 2 +- src/backend/nodes/readfuncs.c | 2 +- src/backend/optimizer/plan/setrefs.c | 30 +++++++++++ src/backend/partitioning/partprune.c | 5 +- src/backend/replication/logical/tablesync.c | 9 +++- src/backend/replication/logical/worker.c | 2 + src/include/executor/execPartition.h | 1 - src/include/executor/executor.h | 24 ++++++++- src/include/nodes/execnodes.h | 23 +++++++- src/include/nodes/plannodes.h | 2 +- src/include/parser/parsetree.h | 10 ---- 32 files changed, 195 insertions(+), 224 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index eeb053d5d8..d20c75367c 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -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->es_range_table_array); 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->es_range_table_array); /* 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->es_range_table_array); 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->es_range_table_array); userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ @@ -2546,10 +2546,6 @@ postgresEndDirectModify(ForeignScanState *node) ReleaseConnection(dmstate->conn); dmstate->conn = NULL; - /* close the target relation. */ - if (dmstate->resultRel) - ExecCloseScanRelation(dmstate->resultRel); - /* MemoryContext will be deleted automatically. */ } @@ -5756,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->es_range_table_array); if (var->varattno == 0) is_wholerow = true; diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0c4a7b6f4f..57efb20d20 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2483,7 +2483,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; + + ExecInitRangeTable(estate, cstate->range_table); /* Set up a tuple slot too */ myslot = ExecInitExtraTupleSlot(estate, tupDesc); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 34f9db93d5..f0d45844c1 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -75,7 +75,8 @@ 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)->es_range_table_array)->updatedCols) /* Local function prototypes */ static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid); diff --git a/src/backend/executor/README b/src/backend/executor/README index 0d7cd552eb..30df410e0e 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -267,8 +267,8 @@ This is a sketch of control flow for full query processing: Per above comments, it's not really critical for ExecEndNode to free any memory; it'll all go away in FreeExecutorState anyway. However, we do need to -be careful to close relations, drop buffer pins, etc, so we do need to scan -the plan state tree to find these sorts of resources. +be careful to drop buffer pins, etc, so we do need to scan the plan state tree +to find these sorts of resources. The executor can also be used to evaluate simple expressions without any Plan diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index f597363eb1..f8ca60ae5a 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -3934,10 +3934,11 @@ 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->es_range_table_array); if (rte->eref) ExecTypeSetColNames(output_tupdesc, rte->eref->colnames); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 3c197c55f4..d895ed29d1 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -111,9 +111,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)->es_range_table_array)->insertedCols) #define GetUpdatedColumns(relinfo, estate) \ - (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols) + (exec_rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table_array)->updatedCols) /* end of local decls */ @@ -837,28 +837,16 @@ InitPlan(QueryDesc *queryDesc, int eflags) */ ExecCheckRTPerms(rangeTable, true); -#ifdef USE_ASSERT_CHECKING - foreach(l, rangeTable) - { - RangeTblEntry *rte = lfirst(l); - - if (rte->rtekind == RTE_RELATION && OidIsValid(rte->relid)) - { - /* - * The following asserts that the necessary lock on the relation - * has already been taken. That can only however be true in the - * parallel leader. - */ - Assert(rte->lockmode != NoLock); - if (!IsParallelWorker() && - !CheckRelationLockedByUs(rte->relid, rte->lockmode)) - elog(WARNING, "InitPlan: lock on \"%s\" not already taken", - get_rel_name(rte->relid)); - } - } -#endif + ExecInitRangeTable(estate, rangeTable); /* + * Allocate an array to store open Relations of each rangeTable item. We + * zero-fill this for now. The Relations are only opened and stored here + * the first time they're required during node initialization. + */ + estate->es_relations = (Relation *) palloc0(estate->es_range_table_size * + sizeof(Relation)); + /* * initialize the node's execution state */ estate->es_range_table = rangeTable; @@ -1501,6 +1489,14 @@ static void ExecEndPlan(PlanState *planstate, EState *estate) { ListCell *l; + int i; + + /* Close range table relations. */ + for (i = 0; i < estate->es_range_table_size; i++) + { + if (estate->es_relations[i]) + heap_close(estate->es_relations[i], NoLock); + } /* * shut down the node-type-specific query processing @@ -1527,18 +1523,6 @@ ExecEndPlan(PlanState *planstate, EState *estate) /* likewise close any trigger target relations */ ExecCleanUpTriggerState(estate); - - /* - * close any relations selected FOR [KEY] UPDATE/SHARE, again keeping - * locks - */ - foreach(l, estate->es_rowMarks) - { - ExecRowMark *erm = (ExecRowMark *) lfirst(l); - - if (erm->relation) - heap_close(erm->relation, NoLock); - } } /* ---------------------------------------------------------------- @@ -2295,24 +2279,20 @@ ExecRowMark * ExecBuildRowMark(EState *estate, PlanRowMark *rc) { ExecRowMark *erm; - Oid relid; Relation relation; Assert(!rc->isParent); - /* get relation's OID (will produce InvalidOid if subquery) */ - relid = getrelid(rc->rti, estate->es_range_table); - switch (rc->markType) { case ROW_MARK_EXCLUSIVE: case ROW_MARK_NOKEYEXCLUSIVE: case ROW_MARK_SHARE: case ROW_MARK_KEYSHARE: - relation = heap_open(relid, NoLock); + relation = ExecRangeTableRelation(estate, rc->rti); break; case ROW_MARK_REFERENCE: - relation = heap_open(relid, NoLock); + relation = ExecRangeTableRelation(estate, rc->rti); break; case ROW_MARK_COPY: /* no physical table access is required */ @@ -2330,7 +2310,7 @@ ExecBuildRowMark(EState *estate, PlanRowMark *rc) erm = (ExecRowMark *) palloc(sizeof(ExecRowMark)); erm->relation = relation; - erm->relid = relid; + erm->relid = exec_rt_fetch(rc->rti, estate->es_range_table_array)->relid; erm->rti = rc->rti; erm->prti = rc->prti; erm->rowmarkId = rc->rowmarkId; @@ -2988,7 +2968,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); + int rtsize = parentestate->es_range_table_size; PlanState *planstate = epqstate->planstate; MemSet(estate->es_epqScanDone, 0, rtsize * sizeof(bool)); @@ -3041,7 +3021,7 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree) MemoryContext oldcontext; ListCell *l; - rtsize = list_length(parentestate->es_range_table); + rtsize = parentestate->es_range_table_size; epqstate->estate = estate = CreateExecutorState(); @@ -3065,6 +3045,9 @@ 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; estate->es_output_cid = parentestate->es_output_cid; @@ -3218,18 +3201,6 @@ EvalPlanQualEnd(EPQState *epqstate) ExecEndNode(subplanstate); } - /* - * close any relations selected FOR [KEY] UPDATE/SHARE, again keeping - * locks - */ - foreach(l, estate->es_rowMarks) - { - ExecRowMark *erm = (ExecRowMark *) lfirst(l); - - if (erm->relation) - heap_close(erm->relation, NoLock); - } - /* throw away the per-estate tuple table */ ExecResetTupleTable(estate->es_tupleTable, false); diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 1d2b3620ee..7fb34d2173 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -861,11 +861,10 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate, int subplan_index = 0; /* - * Remember, proute->partition_dispatch_info[0] corresponds to the root - * partitioned table, which we must not try to close, because it is the - * main target table of the query that will be closed by callers such as - * ExecEndPlan() or DoCopy(). Also, tupslot is NULL for the root - * partitioned table. + * proute->partition_dispatch_info[0] corresponds to the root partitioned + * table, which is the main target table of the query, so it is closed by + * ExecEndModifyTable() or DoCopy(). Also, tupslot is NULL for the root + * partitioned table, so nothing to do here for the root table. */ for (i = 1; i < proute->num_dispatch; i++) { @@ -1418,9 +1417,6 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map) * functions. Details stored include how to map the partition index * returned by the partition pruning code into subplan indexes. * - * ExecDestroyPartitionPruneState: - * Deletes a PartitionPruneState. Must be called during executor shutdown. - * * ExecFindInitialMatchingSubPlans: * Returns indexes of matching subplans. Partition pruning is attempted * without any evaluation of expressions containing PARAM_EXEC Params. @@ -1462,6 +1458,7 @@ PartitionPruneState * ExecCreatePartitionPruneState(PlanState *planstate, PartitionPruneInfo *partitionpruneinfo) { + EState *estate = planstate->state; PartitionPruneState *prunestate; int n_part_hierarchies; ListCell *lc; @@ -1542,7 +1539,7 @@ ExecCreatePartitionPruneState(PlanState *planstate, * so that we can rely on its copies of the table's partition key * and partition descriptor. */ - context->partrel = relation_open(pinfo->reloid, NoLock); + context->partrel = ExecRangeTableRelation(estate, pinfo->rtindex); partkey = RelationGetPartitionKey(context->partrel); partdesc = RelationGetPartitionDesc(context->partrel); @@ -1623,30 +1620,6 @@ ExecCreatePartitionPruneState(PlanState *planstate, } /* - * ExecDestroyPartitionPruneState - * Release resources at plan shutdown. - * - * We don't bother to free any memory here, since the whole executor context - * will be going away shortly. We do need to release our relcache pins. - */ -void -ExecDestroyPartitionPruneState(PartitionPruneState *prunestate) -{ - PartitionPruningData **partprunedata = prunestate->partprunedata; - int i; - - for (i = 0; i < prunestate->num_partprunedata; i++) - { - PartitionPruningData *prunedata = partprunedata[i]; - PartitionedRelPruningData *pprune = prunedata->partrelprunedata; - int j; - - for (j = 0; j < prunedata->num_partrelprunedata; j++) - relation_close(pprune[j].context.partrel, NoLock); - } -} - -/* * ExecFindInitialMatchingSubPlans * Identify the set of subplans that cannot be eliminated by initial * pruning (disregarding any pruning constraints involving PARAM_EXEC diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index d32796aac3..c7f82c83a3 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -25,7 +25,6 @@ * etc * * ExecOpenScanRelation Common code for scan node init routines. - * ExecCloseScanRelation * * executor_errposition Report syntactic position of an error. * @@ -35,6 +34,8 @@ * GetAttributeByName Runtime extraction of columns from tuples. * GetAttributeByNum * + * ExecInitRangeTable Build an array from the range table list to + * allow faster lookups by relid. * NOTES * This file has traditionally been the place to stick misc. * executor support stuff that doesn't really go anyplace else. @@ -653,11 +654,9 @@ Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags) { Relation rel; - Oid reloid; /* Open the relation. */ - reloid = getrelid(scanrelid, estate->es_range_table); - rel = heap_open(reloid, NoLock); + rel = ExecRangeTableRelation(estate, scanrelid); /* * Complain if we're attempting a scan of an unscannable relation, except @@ -675,26 +674,6 @@ ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags) return rel; } -/* ---------------------------------------------------------------- - * ExecCloseScanRelation - * - * Close the heap relation scanned by a base-level scan plan node. - * This should be called during the node's ExecEnd routine. - * - * Currently, we do not release the lock acquired by ExecOpenScanRelation. - * This lock should be held till end of transaction. (There is a faction - * that considers this too much locking, however.) - * - * If we did want to release the lock, we'd have to repeat the logic in - * ExecOpenScanRelation in order to figure out what to release. - * ---------------------------------------------------------------- - */ -void -ExecCloseScanRelation(Relation scanrel) -{ - heap_close(scanrel, NoLock); -} - /* * UpdateChangedParamSet * Add changed parameters to a plan node's chgParam set @@ -997,3 +976,44 @@ ExecCleanTargetListLength(List *targetlist) } return len; } + +/* + * Initialize executor's range table array + */ +void +ExecInitRangeTable(EState *estate, List *rangeTable) +{ + int rti; + ListCell *l; + +#ifdef USE_ASSERT_CHECKING + foreach(l, rangeTable) + { + RangeTblEntry *rte = lfirst(l); + + if (rte->rtekind == RTE_RELATION && OidIsValid(rte->relid)) + { + /* + * The following verifies that the necessary lock on the relation + * has already been taken. That can only however be true in the + * parallel leader. + */ + Assert(rte->lockmode != NoLock); + if (!IsParallelWorker() && + !CheckRelationLockedByUs(rte->relid, rte->lockmode)) + elog(WARNING, "lock on \"%s\" not already taken", + get_rel_name(rte->relid)); + } + } +#endif + + Assert(estate != NULL); + estate->es_range_table = rangeTable; + 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(l, rangeTable) + estate->es_range_table_array[rti++] = lfirst_node(RangeTblEntry, l); +} diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index eb587be221..a16b6da474 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -329,12 +329,6 @@ ExecEndAppend(AppendState *node) */ for (i = 0; i < nplans; i++) ExecEndNode(appendplans[i]); - - /* - * release any resources associated with run-time pruning - */ - if (node->as_prune_state) - ExecDestroyPartitionPruneState(node->as_prune_state); } void diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index baffae27e3..5307cd1b87 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -785,13 +785,11 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node) void ExecEndBitmapHeapScan(BitmapHeapScanState *node) { - Relation relation; HeapScanDesc scanDesc; /* * extract information from the node */ - relation = node->ss.ss_currentRelation; scanDesc = node->ss.ss_currentScanDesc; /* @@ -832,11 +830,6 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node) * close heap scan */ heap_endscan(scanDesc); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(relation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index b816e0b31d..9a33eda688 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -126,10 +126,6 @@ ExecEndCustomScan(CustomScanState *node) /* Clean out the tuple table */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); - - /* Close the heap relation */ - if (node->ss.ss_currentRelation) - ExecCloseScanRelation(node->ss.ss_currentRelation); } void diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index a2a28b7ec2..cf7df72d8c 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -258,10 +258,6 @@ ExecEndForeignScan(ForeignScanState *node) /* clean out the tuple table */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); - - /* close the relation. */ - if (node->ss.ss_currentRelation) - ExecCloseScanRelation(node->ss.ss_currentRelation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4b6d531810..1b530cea40 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -373,14 +373,12 @@ ExecEndIndexOnlyScan(IndexOnlyScanState *node) { Relation indexRelationDesc; IndexScanDesc indexScanDesc; - Relation relation; /* * extract information from the node */ indexRelationDesc = node->ioss_RelationDesc; indexScanDesc = node->ioss_ScanDesc; - relation = node->ss.ss_currentRelation; /* Release VM buffer pin, if any. */ if (node->ioss_VMBuffer != InvalidBuffer) @@ -411,11 +409,6 @@ ExecEndIndexOnlyScan(IndexOnlyScanState *node) index_endscan(indexScanDesc); if (indexRelationDesc) index_close(indexRelationDesc, NoLock); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(relation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 6285a2114e..786e7ac25c 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -802,14 +802,12 @@ ExecEndIndexScan(IndexScanState *node) { Relation indexRelationDesc; IndexScanDesc indexScanDesc; - Relation relation; /* * extract information from the node */ indexRelationDesc = node->iss_RelationDesc; indexScanDesc = node->iss_ScanDesc; - relation = node->ss.ss_currentRelation; /* * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext @@ -833,11 +831,6 @@ ExecEndIndexScan(IndexScanState *node) index_endscan(indexScanDesc); if (indexRelationDesc) index_close(indexRelationDesc, NoLock); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(relation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index efbf4bd18a..ff9606342b 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -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)); diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index d8e0978966..5d0dbb8146 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -363,12 +363,6 @@ ExecEndMergeAppend(MergeAppendState *node) */ for (i = 0; i < nplans; i++) ExecEndNode(mergeplans[i]); - - /* - * release any resources associated with run-time pruning - */ - if (node->ms_prune_state) - ExecDestroyPartitionPruneState(node->ms_prune_state); } void diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index b18f9e3164..c837d36a87 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -2243,10 +2243,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(l, node->resultRelations) { Index resultRelationIndex = lfirst_int(l); - RangeTblEntry *rte = rt_fetch(resultRelationIndex, - estate->es_range_table); - Relation rel = heap_open(rte->relid, NoLock); + Relation rel; + rel = ExecRangeTableRelation(estate, resultRelationIndex); InitResultRelInfo(resultRelInfo, rel, resultRelationIndex, NULL, estate->es_instrument); resultRelInfo++; @@ -2255,17 +2254,18 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) /* If modifying a partitioned table, initialize the root table info */ if (node->rootResultRelIndex >= 0) { - RangeTblEntry *rte; Relation rel; mtstate->rootResultRelInfo = estate->es_root_result_relations + node->rootResultRelIndex; Assert(node->partitionedTarget); - rte = rt_fetch(node->targetRelation, estate->es_range_table); - rel = heap_open(rte->relid, NoLock); - InitResultRelInfo(mtstate->rootResultRelInfo, rel, - node->targetRelation, NULL, estate->es_instrument); + rel = ExecRangeTableRelation(estate, node->targetRelation); + InitResultRelInfo(mtstate->rootResultRelInfo, + rel, + node->targetRelation, + NULL, + estate->es_instrument); } mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans); @@ -2722,15 +2722,13 @@ ExecEndModifyTable(ModifyTableState *node) resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state, resultRelInfo); - /* Close indices and then the relation itself */ + /* + * Close the ResultRelInfo's indexes. The relation itself + * is handled by the executor cleanup code. + */ ExecCloseIndices(resultRelInfo); - heap_close(resultRelInfo->ri_RelationDesc, NoLock); } - /* Close the root partitioned table, if any. */ - if (node->rootResultRelInfo) - heap_close(node->rootResultRelInfo->ri_RelationDesc, NoLock); - /* Close all the partitioned tables, leaf partitions, and their indices */ if (node->mt_partition_tuple_routing) ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing); diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 99528be84a..92ff68a764 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -222,11 +222,6 @@ ExecEndSampleScan(SampleScanState *node) */ if (node->ss.ss_currentScanDesc) heap_endscan(node->ss.ss_currentScanDesc); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(node->ss.ss_currentRelation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index cd53491be0..92ff1e50ed 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -201,13 +201,11 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) void ExecEndSeqScan(SeqScanState *node) { - Relation relation; HeapScanDesc scanDesc; /* * get information from node */ - relation = node->ss.ss_currentRelation; scanDesc = node->ss.ss_currentScanDesc; /* @@ -226,11 +224,6 @@ ExecEndSeqScan(SeqScanState *node) */ if (scanDesc != NULL) heap_endscan(scanDesc); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(relation); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 0cb1946a3e..8cae56f48c 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -489,11 +489,6 @@ ExecEndTidScan(TidScanState *node) */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); - - /* - * close the heap relation. - */ - ExecCloseScanRelation(node->ss.ss_currentRelation); } /* ---------------------------------------------------------------- diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index da02b8961c..dd76284676 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1190,7 +1190,7 @@ _copyPartitionedRelPruneInfo(const PartitionedRelPruneInfo *from) { PartitionedRelPruneInfo *newnode = makeNode(PartitionedRelPruneInfo); - COPY_SCALAR_FIELD(reloid); + COPY_SCALAR_FIELD(rtindex); COPY_NODE_FIELD(pruning_steps); COPY_BITMAPSET_FIELD(present_parts); COPY_SCALAR_FIELD(nparts); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 2e6a5a1b42..6aefdd8bc4 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1029,7 +1029,7 @@ _outPartitionedRelPruneInfo(StringInfo str, const PartitionedRelPruneInfo *node) WRITE_NODE_TYPE("PARTITIONEDRELPRUNEINFO"); - WRITE_OID_FIELD(reloid); + WRITE_UINT_FIELD(rtindex); WRITE_NODE_FIELD(pruning_steps); WRITE_BITMAPSET_FIELD(present_parts); WRITE_INT_FIELD(nparts); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index aea5b0abc4..30609c96e6 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -2373,7 +2373,7 @@ _readPartitionedRelPruneInfo(void) { READ_LOCALS(PartitionedRelPruneInfo); - READ_OID_FIELD(reloid); + READ_UINT_FIELD(rtindex); READ_NODE_FIELD(pruning_steps); READ_BITMAPSET_FIELD(present_parts); READ_INT_FIELD(nparts); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 17d53f356c..ee01ef82d2 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -890,6 +890,21 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) (Plan *) lfirst(l), rtoffset); } + if (splan->part_prune_info) + { + foreach(l, splan->part_prune_info->prune_infos) + { + List *prune_infos = lfirst(l); + ListCell *l2; + + foreach(l2, prune_infos) + { + PartitionedRelPruneInfo *pinfo = lfirst(l2); + + pinfo->rtindex += rtoffset; + } + } + } } break; case T_MergeAppend: @@ -908,6 +923,21 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) (Plan *) lfirst(l), rtoffset); } + if (splan->part_prune_info) + { + foreach(l, splan->part_prune_info->prune_infos) + { + List *prune_infos = lfirst(l); + ListCell *l2; + + foreach(l2, prune_infos) + { + PartitionedRelPruneInfo *pinfo = lfirst(l2); + + pinfo->rtindex += rtoffset; + } + } + } } break; case T_RecursiveUnion: diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index e1ce8b4ddc..1763dd67a3 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -357,7 +357,6 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, Index rti = lfirst_int(lc); RelOptInfo *subpart = find_base_rel(root, rti); PartitionedRelPruneInfo *pinfo; - RangeTblEntry *rte; Bitmapset *present_parts; int nparts = subpart->nparts; int partnatts = subpart->part_scheme->partnatts; @@ -459,10 +458,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel, present_parts = bms_add_member(present_parts, i); } - rte = root->simple_rte_array[subpart->relid]; - pinfo = makeNode(PartitionedRelPruneInfo); - pinfo->reloid = rte->relid; + pinfo->rtindex = rti; pinfo->pruning_steps = pruning_steps; pinfo->present_parts = present_parts; pinfo->nparts = nparts; diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c index 905a983074..d72f6a1b4f 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -795,7 +795,14 @@ copy_table(Relation rel) copybuf = makeStringInfo(); pstate = make_parsestate(NULL); - addRangeTableEntryForRelation(pstate, rel, NULL, false, false, NoLock); + + /* + * Caller LogicalRepSyncTableStart took RowExclusiveLock, which we must + * record in the RTE being built, because executor initialization invoked + * by copy.c expects it. + */ + addRangeTableEntryForRelation(pstate, rel, NULL, false, false, + RowExclusiveLock); attnamelist = make_copy_attnamelist(relmapentry); cstate = BeginCopyFrom(pstate, rel, NULL, false, copy_read_data, attnamelist, NIL); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 42345f94d3..c63de66857 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -200,6 +200,8 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel) rte->relid = RelationGetRelid(rel->localrel); rte->relkind = rel->localrel->rd_rel->relkind; estate->es_range_table = list_make1(rte); + estate->es_range_table_array = &rte; + estate->es_range_table_size = 1; resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0); diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 89ce53815c..04847d639d 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -197,7 +197,6 @@ extern void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute); extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate, PartitionPruneInfo *partitionpruneinfo); -extern void ExecDestroyPartitionPruneState(PartitionPruneState *prunestate); extern Bitmapset *ExecFindMatchingSubPlans(PartitionPruneState *prunestate); extern Bitmapset *ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans); diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 1d2b48fe09..b03d8c591f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -17,6 +17,7 @@ #include "executor/execdesc.h" #include "nodes/parsenodes.h" #include "utils/memutils.h" +#include "utils/rel.h" /* @@ -478,6 +479,7 @@ extern ExprContext *CreateExprContext(EState *estate); extern ExprContext *CreateStandaloneExprContext(void); extern void FreeExprContext(ExprContext *econtext, bool isCommit); extern void ReScanExprContext(ExprContext *econtext); +extern void ExecInitRangeTable(EState *estate, List *rangeTable); #define ResetExprContext(econtext) \ MemoryContextReset((econtext)->ecxt_per_tuple_memory) @@ -513,7 +515,6 @@ extern void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid); extern Relation ExecOpenScanRelation(EState *estate, Index scanrelid, int eflags); -extern void ExecCloseScanRelation(Relation scanrel); extern int executor_errposition(EState *estate, int location); @@ -568,4 +569,25 @@ extern void CheckCmdReplicaIdentity(Relation rel, CmdType cmd); extern void CheckSubscriptionRelkind(char relkind, const char *nspname, const char *relname); +#ifndef FRONTEND +static inline Relation +ExecRangeTableRelation(EState *estate, Index rti) +{ + Relation rel = estate->es_relations[rti - 1]; + + if (rel == NULL) + { + RangeTblEntry *rte = exec_rt_fetch(rti, estate->es_range_table_array); + + /* + * No need to lock the relation. Locks were already + * obtained by the upstream code. + */ + rel = estate->es_relations[rti - 1] = heap_open(rte->relid, NoLock); + } + + return rel; +} +#endif + #endif /* EXECUTOR_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 03ad516976..639235d622 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -20,6 +20,7 @@ #include "executor/instrument.h" #include "lib/pairingheap.h" #include "nodes/params.h" +#include "nodes/parsenodes.h" #include "nodes/plannodes.h" #include "utils/hsearch.h" #include "utils/queryenvironment.h" @@ -478,7 +479,11 @@ typedef struct EState ScanDirection es_direction; /* current scan direction */ Snapshot es_snapshot; /* time qual to use */ Snapshot es_crosscheck_snapshot; /* crosscheck time qual for RI */ - List *es_range_table; /* List of RangeTblEntry */ + List *es_range_table; /* List of RangeTblEntry */ + struct RangeTblEntry **es_range_table_array; /* 0-based range table */ + int es_range_table_size; /* size of the range table array */ + Relation *es_relations; /* 0-based array of Relations + * es_range_table_size elements in length. */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ @@ -579,6 +584,22 @@ typedef struct EState struct JitInstrumentation *es_jit_combined_instr; } EState; +/* + * exec_rt_fetch + * + * NB: this will crash and burn if handed an out-of-range RT index + */ +#define exec_rt_fetch(rangetblidx, rangetbl) rangetbl[(rangetblidx) - 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) \ + (exec_rt_fetch(rangeindex, rangetable)->relid) /* * ExecRowMark - diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 27ae73e3d4..452627dd03 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1093,7 +1093,7 @@ typedef struct PartitionPruneInfo typedef struct PartitionedRelPruneInfo { NodeTag type; - Oid reloid; /* OID of partition rel for this level */ + Index rtindex; /* Partition table's RT index */ List *pruning_steps; /* List of PartitionPruneStep, see below */ Bitmapset *present_parts; /* Indexes of all partitions which subplans or * subparts are present for. */ diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h index dd9ae658ac..fe16d7d1fa 100644 --- a/src/include/parser/parsetree.h +++ b/src/include/parser/parsetree.h @@ -32,16 +32,6 @@ ((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. */ -- 2.11.0