On 2018/06/14 23:40, Tom Lane wrote:
> Robert Haas <robertmh...@gmail.com> writes:
>> On Thu, Jun 14, 2018 at 7:23 AM, David Rowley
>> <david.row...@2ndquadrant.com> wrote:
>>> However, I only spent about 10 mins looking into this, there may be
>>> some giant holes in the idea.  It would need much more research.
> 
>> It kind of flies in the face of the idea that a RangeTblEntry is just
>> a node that can be freely copied around, serialized and deserialized,
>> etc.
> 
> And also the idea that the Plan tree is read-only to the executor,
> which is not a good property to give up.
> 
>> I think it would be better to keep the pointer in the RelOptInfo in
>> the planner and in the EState or PlanState in the executor.  Those are
>> things we don't think can be copied, serialized, etc.
> 
> Yeah, RelOptInfo seems like the natural place in the planner; we might
> need index relcache links in IndexOptInfo, too.
> 
> I'm less sure what to do in the executor.  We already do keep open
> relation pointers in PlanStates; the problem is just that it's
> node-type-specific (ss_currentRelation, iss_RelationDesc, etc).  Perhaps
> that's unavoidable and we should just add more such fields as needed.

The patch I mentioned upthread maintains an array of Relation pointers in
AppendState with as many members as there are in the partitioned_rels list
that appears in the corresponding Append plan.

I revised that patch a bit to rename ExecLockNonLeafAppendTables to
ExecOpenAppendPartitionedTables to sound consistent with
ExecOpenScanRelation et al.

Thanks,
Amit
From d55c30f8330081bc7c2eadea61d236a3b5de0f87 Mon Sep 17 00:00:00 2001
From: amit <amitlangot...@gmail.com>
Date: Thu, 14 Jun 2018 14:50:22 +0900
Subject: [PATCH] Open partitioned tables during Append initialization

---
 src/backend/executor/execPartition.c   |  40 ++------
 src/backend/executor/execUtils.c       | 173 ++++++++++++++++++++++-----------
 src/backend/executor/nodeAppend.c      |  33 ++++---
 src/backend/executor/nodeMergeAppend.c |   9 +-
 src/include/executor/execPartition.h   |   4 +-
 src/include/executor/executor.h        |   4 +-
 src/include/nodes/execnodes.h          |   2 +
 7 files changed, 157 insertions(+), 108 deletions(-)

diff --git a/src/backend/executor/execPartition.c 
b/src/backend/executor/execPartition.c
index 7a4665cc4e..ea6b05934b 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1401,7 +1401,8 @@ adjust_partition_tlist(List *tlist, TupleConversionMap 
*map)
  * in each PartitionPruneInfo.
  */
 PartitionPruneState *
-ExecCreatePartitionPruneState(PlanState *planstate, List *partitionpruneinfo)
+ExecCreatePartitionPruneState(PlanState *planstate, List *partitionpruneinfo,
+                                                         Relation 
*partitioned_rels)
 {
        PartitionPruneState *prunestate;
        PartitionPruningData *prunedata;
@@ -1440,6 +1441,7 @@ ExecCreatePartitionPruneState(PlanState *planstate, List 
*partitionpruneinfo)
                PartitionPruneInfo *pinfo = castNode(PartitionPruneInfo, 
lfirst(lc));
                PartitionPruningData *pprune = &prunedata[i];
                PartitionPruneContext *context = &pprune->context;
+               Relation        rel;
                PartitionDesc partdesc;
                PartitionKey partkey;
                int                     partnatts;
@@ -1460,17 +1462,11 @@ ExecCreatePartitionPruneState(PlanState *planstate, 
List *partitionpruneinfo)
                /* present_parts is also subject to later modification */
                pprune->present_parts = bms_copy(pinfo->present_parts);
 
-               /*
-                * We need to hold a pin on the partitioned table's relcache 
entry so
-                * that we can rely on its copies of the table's partition key 
and
-                * partition descriptor.  We need not get a lock though; one 
should
-                * have been acquired already by InitPlan or
-                * ExecLockNonLeafAppendTables.
-                */
-               context->partrel = relation_open(pinfo->reloid, NoLock);
-
-               partkey = RelationGetPartitionKey(context->partrel);
-               partdesc = RelationGetPartitionDesc(context->partrel);
+               Assert(partitioned_rels[i] != NULL);
+               rel = partitioned_rels[i];
+               Assert(RelationGetRelid(rel) == pinfo->reloid);
+               partkey = RelationGetPartitionKey(rel);
+               partdesc = RelationGetPartitionDesc(rel);
                n_steps = list_length(pinfo->pruning_steps);
 
                context->strategy = partkey->strategy;
@@ -1546,26 +1542,6 @@ ExecCreatePartitionPruneState(PlanState *planstate, List 
*partitionpruneinfo)
 }
 
 /*
- * 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)
-{
-       int                     i;
-
-       for (i = 0; i < prunestate->num_partprunedata; i++)
-       {
-               PartitionPruningData *pprune = &prunestate->partprunedata[i];
-
-               relation_close(pprune->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 b963cae730..540b885c55 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -704,6 +704,124 @@ ExecCloseScanRelation(Relation scanrel)
 }
 
 /*
+ * ExecOpenAppendPartitionedTables
+ *
+ * Opens and/or locks, if necessary, the tables indicated by the RT indexes
+ * contained in the partitioned_rels list of a given Append or MergeAppend
+ * node.  These are the non-leaf tables in the partition tree controlled by
+ * the node.
+ */
+void
+ExecOpenAppendPartitionedTables(PlanState *planstate,
+                                                               EState *estate,
+                                                               List 
*partitioned_rels)
+{
+       PlannedStmt *stmt = estate->es_plannedstmt;
+       ListCell   *lc;
+       int                     i;
+
+       Assert(partitioned_rels != NIL);
+
+       /*
+        * If we're going to be performing pruning, allocate space for Relation
+        * pointers to be used later when setting up partition pruning state in
+        * ExecCreatePartitionPruneState.
+        */
+       if (IsA(planstate, AppendState))
+       {
+               AppendState *appendstate = (AppendState *) planstate;
+               Append *node = (Append *) planstate->plan;
+
+               if (node->part_prune_infos != NIL)
+               {
+                       Assert(list_length(node->part_prune_infos) ==
+                                  list_length(partitioned_rels));
+                       appendstate->partitioned_rels = (Relation *)
+                                                               
palloc(sizeof(Relation) *
+                                                                          
list_length(node->part_prune_infos));
+                       appendstate->num_partitioned_rels =
+                                                                          
list_length(node->part_prune_infos);
+               }
+       }
+
+       i = 0;
+       foreach(lc, partitioned_rels)
+       {
+               ListCell   *l;
+               Index           rti = lfirst_int(lc);
+               bool            is_result_rel = false;
+               Oid                     relid = getrelid(rti, 
estate->es_range_table);
+               int                     lockmode;
+
+               /*
+                * We need not bother about partitioned result relations here, 
they're
+                * taken care of in InitPlan.
+                */
+               foreach(l, stmt->nonleafResultRelations)
+               {
+                       if (rti == lfirst_int(l))
+                       {
+                               is_result_rel = true;
+                               break;
+                       }
+               }
+
+               /*
+                * Not a result relation; check if there is a RowMark that 
requires
+                * taking a RowShareLock on this rel.
+                */
+               if (!is_result_rel)
+               {
+                       PlanRowMark *rc = NULL;
+
+                       foreach(l, stmt->rowMarks)
+                       {
+                               if (((PlanRowMark *) lfirst(l))->rti == rti)
+                               {
+                                       rc = lfirst(l);
+                                       break;
+                               }
+                       }
+
+                       if (rc && RowMarkRequiresRowShareLock(rc->markType))
+                               lockmode = RowShareLock;
+                       else
+                               lockmode = AccessShareLock;
+                       switch (nodeTag(planstate))
+                       {
+                               /*
+                                * We need to also hold a pin on the 
partitioned table's
+                                * relcache entry so that we can rely on its 
copies of the
+                                * table's partition key and partition 
descriptor, which
+                                * are used when setting up partition pruning 
state.
+                                */
+                               case T_AppendState:
+                                       {
+                                               AppendState *appendstate = 
(AppendState *) planstate;
+
+                                               if 
(appendstate->partitioned_rels)
+                                                       
appendstate->partitioned_rels[i] =
+                                                                               
                heap_open(relid, lockmode);
+                                               else
+                                                       LockRelationOid(relid, 
lockmode);
+                                               i++;
+                                       }
+
+                               /* Just lock here; there is no pruning support. 
*/
+                               case T_MergeAppendState:
+                                       LockRelationOid(relid, lockmode);
+                                       break;
+
+                               default:
+                                       elog(ERROR, "invalid PlanState node: 
%d",
+                                                nodeTag(planstate));
+                                       break;
+                       }
+               }
+       }
+}
+
+/*
  * UpdateChangedParamSet
  *             Add changed parameters to a plan node's chgParam set
  */
@@ -856,61 +974,6 @@ ShutdownExprContext(ExprContext *econtext, bool isCommit)
 }
 
 /*
- * ExecLockNonLeafAppendTables
- *
- * Locks, if necessary, the tables indicated by the RT indexes contained in
- * the partitioned_rels list.  These are the non-leaf tables in the partition
- * tree controlled by a given Append or MergeAppend node.
- */
-void
-ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
-{
-       PlannedStmt *stmt = estate->es_plannedstmt;
-       ListCell   *lc;
-
-       foreach(lc, partitioned_rels)
-       {
-               ListCell   *l;
-               Index           rti = lfirst_int(lc);
-               bool            is_result_rel = false;
-               Oid                     relid = getrelid(rti, 
estate->es_range_table);
-
-               /* If this is a result relation, already locked in InitPlan */
-               foreach(l, stmt->nonleafResultRelations)
-               {
-                       if (rti == lfirst_int(l))
-                       {
-                               is_result_rel = true;
-                               break;
-                       }
-               }
-
-               /*
-                * Not a result relation; check if there is a RowMark that 
requires
-                * taking a RowShareLock on this rel.
-                */
-               if (!is_result_rel)
-               {
-                       PlanRowMark *rc = NULL;
-
-                       foreach(l, stmt->rowMarks)
-                       {
-                               if (((PlanRowMark *) lfirst(l))->rti == rti)
-                               {
-                                       rc = lfirst(l);
-                                       break;
-                               }
-                       }
-
-                       if (rc && RowMarkRequiresRowShareLock(rc->markType))
-                               LockRelationOid(relid, RowShareLock);
-                       else
-                               LockRelationOid(relid, AccessShareLock);
-               }
-       }
-}
-
-/*
  *             GetAttributeByName
  *             GetAttributeByNum
  *
diff --git a/src/backend/executor/nodeAppend.c 
b/src/backend/executor/nodeAppend.c
index 5ce4fb43e1..24604aa311 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -113,18 +113,17 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
        Assert(!(eflags & EXEC_FLAG_MARK));
 
        /*
-        * Lock the non-leaf tables in the partition tree controlled by this 
node.
-        * It's a no-op for non-partitioned parent tables.
-        */
-       ExecLockNonLeafAppendTables(node->partitioned_rels, estate);
-
-       /*
         * create new AppendState for our append node
         */
        appendstate->ps.plan = (Plan *) node;
        appendstate->ps.state = estate;
        appendstate->ps.ExecProcNode = ExecAppend;
 
+       /* Lock and open (if needed) the partitioned tables. */
+       if (node->partitioned_rels != NIL)
+               ExecOpenAppendPartitionedTables((PlanState *) appendstate, 
estate,
+                                                                               
node->partitioned_rels);
+
        /* Let choose_next_subplan_* function handle setting the first subplan 
*/
        appendstate->as_whichplan = INVALID_SUBPLAN_INDEX;
 
@@ -137,8 +136,12 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
                ExecAssignExprContext(estate, &appendstate->ps);
 
                /* Create the working data structure for pruning. */
+
+               /* ExecLockNonLeafAppendTables must have set this up. */
+               Assert(appendstate->partitioned_rels != NULL);
                prunestate = ExecCreatePartitionPruneState(&appendstate->ps,
-                                                                               
                   node->part_prune_infos);
+                                                                               
                   node->part_prune_infos,
+                                                                               
                   appendstate->partitioned_rels);
                appendstate->as_prune_state = prunestate;
 
                /* Perform an initial partition prune, if required. */
@@ -318,6 +321,7 @@ ExecEndAppend(AppendState *node)
        PlanState **appendplans;
        int                     nplans;
        int                     i;
+       int                     num_partitioned_rels;
 
        /*
         * get information from the node
@@ -326,16 +330,19 @@ ExecEndAppend(AppendState *node)
        nplans = node->as_nplans;
 
        /*
+        * Close partitioned rels that we may have opened for partition
+        * pruning.
+        */
+       num_partitioned_rels = node->num_partitioned_rels;
+       Assert(node->partitioned_rels != NULL || num_partitioned_rels == 0);
+       for (i = 0; i < num_partitioned_rels; i++)
+               heap_close(node->partitioned_rels[i], NoLock);
+
+       /*
         * shut down each of the subscans
         */
        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/nodeMergeAppend.c 
b/src/backend/executor/nodeMergeAppend.c
index 118f4ef07d..60ce18ea95 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -72,11 +72,10 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int 
eflags)
        /* check for unsupported flags */
        Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
 
-       /*
-        * Lock the non-leaf tables in the partition tree controlled by this 
node.
-        * It's a no-op for non-partitioned parent tables.
-        */
-       ExecLockNonLeafAppendTables(node->partitioned_rels, estate);
+       /* Lock and open (if needed) the partitioned tables. */
+       if (node->partitioned_rels != NIL)
+               ExecOpenAppendPartitionedTables((PlanState *) mergestate, 
estate,
+                                                                               
node->partitioned_rels);
 
        /*
         * Set up empty vector of subplan states
diff --git a/src/include/executor/execPartition.h 
b/src/include/executor/execPartition.h
index 862bf65060..33c3f73e78 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -209,8 +209,8 @@ extern HeapTuple 
ConvertPartitionTupleSlot(TupleConversionMap *map,
 extern void ExecCleanupTupleRouting(ModifyTableState *mtstate,
                                                PartitionTupleRouting *proute);
 extern PartitionPruneState *ExecCreatePartitionPruneState(PlanState *planstate,
-                                                         List 
*partitionpruneinfo);
-extern void ExecDestroyPartitionPruneState(PartitionPruneState *prunestate);
+                                                         List 
*partitionpruneinfo,
+                                                         Relation 
*partitioned_rels);
 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 f82b51667f..eb6bc9d14f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -524,7 +524,9 @@ extern void UnregisterExprContextCallback(ExprContext 
*econtext,
                                                          
ExprContextCallbackFunction function,
                                                          Datum arg);
 
-extern void ExecLockNonLeafAppendTables(List *partitioned_rels, EState 
*estate);
+extern void ExecOpenAppendPartitionedTables(PlanState *planstate,
+                                                               EState *estate,
+                                                               List 
*partitioned_rels);
 
 extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
                                   bool *isNull);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index da7f52cab0..a20d94fd9f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1092,6 +1092,8 @@ struct AppendState
                                                                                
 * the first partial plan */
        ParallelAppendState *as_pstate; /* parallel coordination info */
        Size            pstate_len;             /* size of parallel 
coordination info */
+       Relation   *partitioned_rels;
+       int                     num_partitioned_rels;   /* number of entries in 
above array */
        struct PartitionPruneState *as_prune_state;
        Bitmapset  *as_valid_subplans;
        bool            (*choose_next_subplan) (AppendState *);
-- 
2.11.0

Reply via email to