Re: [HACKERS] [BUGS] [postgresql 10 beta3] unrecognized node type: 90

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: [HACKERS] [BUGS] [postgresql 10 beta3] unrecognized node type: 90
Дата
Msg-id 24667.1503865781@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: [HACKERS] [BUGS] [postgresql 10 beta3] unrecognized node type: 90  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: [HACKERS] [BUGS] [postgresql 10 beta3] unrecognized node type: 90  (Amit Kapila <amit.kapila16@gmail.com>)
Список pgsql-hackers
I wrote:
> I think that the correct fix probably involves marking each parallel scan
> plan node as dependent on a pseudo executor parameter, which the parent
> Gather or GatherMerge node would flag as being changed on each rescan.
> This would cue the plan layers in between that they cannot optimize on the
> assumption that the leader's instance of the parallel scan will produce
> exactly the same rows as it did last time, even when "nothing else
> changed".  The "wtParam" pseudo parameter that's used for communication
> between RecursiveUnion and its descendant WorkTableScan node is a good
> model for what needs to happen.

Here is a draft patch for this.  It's a bit different from wtParam in
that the special parameter isn't allocated until createplan.c time,
so that we don't eat a parameter slot if we end up choosing a non-parallel
plan; but otherwise things are comparable.

I could use some feedback on whether this is marking dependent child nodes
sanely.  As written, any plan node that's marked parallel_aware is assumed
to need a dependency on the parent Gather or GatherMerge's rescan param
--- and the planner will now bitch if a parallel_aware plan node is not
under any such Gather.  Is this reasonable?  I do not see any
documentation that defines the parallel_aware field with enough clarity
to be very sure about it.

I included the heap_parallelscan_nextpage hack I'm using to make the
original failure reproducible, but that hunk is not meant for commit.
Also, the regression test case is the same as in a2b70c89c.

            regards, tom lane

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index ff03c68..c478897 100644
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
*************** heap_parallelscan_nextpage(HeapScanDesc
*** 1763,1768 ****
--- 1763,1770 ----
              ss_report_location(scan->rs_rd, parallel_scan->phs_startblock);
      }

+     pg_usleep(random()/1000000);
+
      return page;
  }

diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index e8d94ee..3aa819f 100644
*** a/src/backend/executor/nodeGather.c
--- b/src/backend/executor/nodeGather.c
*************** ExecShutdownGather(GatherState *node)
*** 430,435 ****
--- 430,438 ----
  void
  ExecReScanGather(GatherState *node)
  {
+     Gather       *gather = (Gather *) node->ps.plan;
+     PlanState  *outerPlan = outerPlanState(node);
+
      /*
       * Re-initialize the parallel workers to perform rescan of relation. We
       * want to gracefully shutdown all the workers so that they should be able
*************** ExecReScanGather(GatherState *node)
*** 443,447 ****
      if (node->pei)
          ExecParallelReinitialize(node->pei);

!     ExecReScan(node->ps.lefttree);
  }
--- 446,467 ----
      if (node->pei)
          ExecParallelReinitialize(node->pei);

!     /*
!      * Set child node's chgParam to tell it that the next scan might deliver a
!      * different set of rows within the leader process.  (The overall rowset
!      * shouldn't change, but the leader process's subset might; hence nodes
!      * between here and the parallel table scan node mustn't optimize on the
!      * assumption of an unchanging rowset.)
!      */
!     if (gather->rescan_param >= 0)
!         outerPlan->chgParam = bms_add_member(outerPlan->chgParam,
!                                              gather->rescan_param);
!
!
!     /*
!      * if chgParam of subnode is not null then plan will be re-scanned by
!      * first ExecProcNode.
!      */
!     if (outerPlan->chgParam == NULL)
!         ExecReScan(outerPlan);
  }
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 64c6239..e8c70df 100644
*** a/src/backend/executor/nodeGatherMerge.c
--- b/src/backend/executor/nodeGatherMerge.c
*************** ExecShutdownGatherMergeWorkers(GatherMer
*** 325,330 ****
--- 325,333 ----
  void
  ExecReScanGatherMerge(GatherMergeState *node)
  {
+     GatherMerge *gm = (GatherMerge *) node->ps.plan;
+     PlanState  *outerPlan = outerPlanState(node);
+
      /*
       * Re-initialize the parallel workers to perform rescan of relation. We
       * want to gracefully shutdown all the workers so that they should be able
*************** ExecReScanGatherMerge(GatherMergeState *
*** 339,345 ****
      if (node->pei)
          ExecParallelReinitialize(node->pei);

!     ExecReScan(node->ps.lefttree);
  }

  /*
--- 342,365 ----
      if (node->pei)
          ExecParallelReinitialize(node->pei);

!     /*
!      * Set child node's chgParam to tell it that the next scan might deliver a
!      * different set of rows within the leader process.  (The overall rowset
!      * shouldn't change, but the leader process's subset might; hence nodes
!      * between here and the parallel table scan node mustn't optimize on the
!      * assumption of an unchanging rowset.)
!      */
!     if (gm->rescan_param >= 0)
!         outerPlan->chgParam = bms_add_member(outerPlan->chgParam,
!                                              gm->rescan_param);
!
!
!     /*
!      * if chgParam of subnode is not null then plan will be re-scanned by
!      * first ExecProcNode.
!      */
!     if (outerPlan->chgParam == NULL)
!         ExecReScan(outerPlan);
  }

  /*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7204169..f9ddf4e 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyGather(const Gather *from)
*** 361,366 ****
--- 361,367 ----
       * copy remainder of node
       */
      COPY_SCALAR_FIELD(num_workers);
+     COPY_SCALAR_FIELD(rescan_param);
      COPY_SCALAR_FIELD(single_copy);
      COPY_SCALAR_FIELD(invisible);

*************** _copyGatherMerge(const GatherMerge *from
*** 384,389 ****
--- 385,391 ----
       * copy remainder of node
       */
      COPY_SCALAR_FIELD(num_workers);
+     COPY_SCALAR_FIELD(rescan_param);
      COPY_SCALAR_FIELD(numCols);
      COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
      COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5ce3c7c..9ee3e23 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outGather(StringInfo str, const Gather
*** 479,484 ****
--- 479,485 ----
      _outPlanInfo(str, (const Plan *) node);

      WRITE_INT_FIELD(num_workers);
+     WRITE_INT_FIELD(rescan_param);
      WRITE_BOOL_FIELD(single_copy);
      WRITE_BOOL_FIELD(invisible);
  }
*************** _outGatherMerge(StringInfo str, const Ga
*** 493,498 ****
--- 494,500 ----
      _outPlanInfo(str, (const Plan *) node);

      WRITE_INT_FIELD(num_workers);
+     WRITE_INT_FIELD(rescan_param);
      WRITE_INT_FIELD(numCols);

      appendStringInfoString(str, " :sortColIdx");
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 86c811d..67b9e19 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readGather(void)
*** 2163,2168 ****
--- 2163,2169 ----
      ReadCommonPlan(&local_node->plan);

      READ_INT_FIELD(num_workers);
+     READ_INT_FIELD(rescan_param);
      READ_BOOL_FIELD(single_copy);
      READ_BOOL_FIELD(invisible);

*************** _readGatherMerge(void)
*** 2180,2185 ****
--- 2181,2187 ----
      ReadCommonPlan(&local_node->plan);

      READ_INT_FIELD(num_workers);
+     READ_INT_FIELD(rescan_param);
      READ_INT_FIELD(numCols);
      READ_ATTRNUMBER_ARRAY(sortColIdx, local_node->numCols);
      READ_OID_ARRAY(sortOperators, local_node->numCols);
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index fc0fca4..62242e8 100644
*** a/src/backend/optimizer/README
--- b/src/backend/optimizer/README
*************** RelOptInfo      - a relation or joined r
*** 374,379 ****
--- 374,380 ----
    MaterialPath  - a Material plan node
    UniquePath    - remove duplicate rows (either by hashing or sorting)
    GatherPath    - collect the results of parallel workers
+   GatherMergePath - collect parallel results, preserving their common sort order
    ProjectionPath - a Result plan node with child (used for projection)
    ProjectSetPath - a ProjectSet plan node applied to some sub-path
    SortPath      - a Sort plan node applied to some sub-path
*************** either by an entire query or some portio
*** 1030,1036 ****
  some of that work can be done by one or more worker processes, which are
  called parallel workers.  Parallel workers are a subtype of dynamic
  background workers; see src/backend/access/transam/README.parallel for a
! fuller description.  Academic literature on parallel query suggests that
  that parallel execution strategies can be divided into essentially two
  categories: pipelined parallelism, where the execution of the query is
  divided into multiple stages and each stage is handled by a separate
--- 1031,1037 ----
  some of that work can be done by one or more worker processes, which are
  called parallel workers.  Parallel workers are a subtype of dynamic
  background workers; see src/backend/access/transam/README.parallel for a
! fuller description.  The academic literature on parallel query suggests
  that parallel execution strategies can be divided into essentially two
  categories: pipelined parallelism, where the execution of the query is
  divided into multiple stages and each stage is handled by a separate
*************** that the underlying table be partitioned
*** 1046,1061 ****
  there is some method of dividing the data from at least one of the base
  tables involved in the relation across multiple processes, (2) allowing
  each process to handle its own portion of the data, and then (3)
! collecting the results.  Requirements (2) and (3) is satisfied by the
! executor node Gather, which launches any number of worker processes and
! executes its single child plan in all of them (and perhaps in the leader
! also, if the children aren't generating enough data to keep the leader
! busy).  Requirement (1) is handled by the SeqScan node: when invoked
! with parallel_aware = true, this node will, in effect, partition the
! table on a block by block basis, returning a subset of the tuples from
! the relation in each worker where that SeqScan is executed.  A similar
! scheme could be (and probably should be) implemented for bitmap heap
! scans.

  Just as we do for non-parallel access methods, we build Paths to
  represent access strategies that can be used in a parallel plan.  These
--- 1047,1060 ----
  there is some method of dividing the data from at least one of the base
  tables involved in the relation across multiple processes, (2) allowing
  each process to handle its own portion of the data, and then (3)
! collecting the results.  Requirements (2) and (3) are satisfied by the
! executor node Gather (or GatherMerge), which launches any number of worker
! processes and executes its single child plan in all of them, and perhaps
! in the leader also, if the children aren't generating enough data to keep
! the leader busy.  Requirement (1) is handled by the table scan node: when
! invoked with parallel_aware = true, this node will, in effect, partition
! the table on a block by block basis, returning a subset of the tuples from
! the relation in each worker where that scan node is executed.

  Just as we do for non-parallel access methods, we build Paths to
  represent access strategies that can be used in a parallel plan.  These
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 5c934f2..2821662 100644
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** static Unique *make_unique_from_sortclau
*** 267,273 ****
  static Unique *make_unique_from_pathkeys(Plan *lefttree,
                            List *pathkeys, int numCols);
  static Gather *make_gather(List *qptlist, List *qpqual,
!             int nworkers, bool single_copy, Plan *subplan);
  static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
             List *distinctList, AttrNumber flagColIdx, int firstFlag,
             long numGroups);
--- 267,273 ----
  static Unique *make_unique_from_pathkeys(Plan *lefttree,
                            List *pathkeys, int numCols);
  static Gather *make_gather(List *qptlist, List *qpqual,
!             int nworkers, int rescan_param, bool single_copy, Plan *subplan);
  static SetOp *make_setop(SetOpCmd cmd, SetOpStrategy strategy, Plan *lefttree,
             List *distinctList, AttrNumber flagColIdx, int firstFlag,
             long numGroups);
*************** create_gather_plan(PlannerInfo *root, Ga
*** 1471,1476 ****
--- 1471,1477 ----
      gather_plan = make_gather(tlist,
                                NIL,
                                best_path->num_workers,
+                               SS_assign_special_param(root),
                                best_path->single_copy,
                                subplan);

*************** create_gather_merge_plan(PlannerInfo *ro
*** 1505,1510 ****
--- 1506,1514 ----
      gm_plan->num_workers = best_path->num_workers;
      copy_generic_path_info(&gm_plan->plan, &best_path->path);

+     /* Assign the rescan Param. */
+     gm_plan->rescan_param = SS_assign_special_param(root);
+
      /* Gather Merge is pointless with no pathkeys; use Gather instead. */
      Assert(pathkeys != NIL);

*************** static Gather *
*** 6238,6243 ****
--- 6242,6248 ----
  make_gather(List *qptlist,
              List *qpqual,
              int nworkers,
+             int rescan_param,
              bool single_copy,
              Plan *subplan)
  {
*************** make_gather(List *qptlist,
*** 6249,6254 ****
--- 6254,6260 ----
      plan->lefttree = subplan;
      plan->righttree = NULL;
      node->num_workers = nworkers;
+     node->rescan_param = rescan_param;
      node->single_copy = single_copy;
      node->invisible = false;

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fdef00a..9662302 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** standard_planner(Query *parse, int curso
*** 375,380 ****
--- 375,386 ----
          gather->invisible = (force_parallel_mode == FORCE_PARALLEL_REGRESS);

          /*
+          * Since this Gather has no parallel-aware descendants to signal to,
+          * we don't need a rescan Param.
+          */
+         gather->rescan_param = -1;
+
+         /*
           * Ideally we'd use cost_gather here, but setting up dummy path data
           * to satisfy it doesn't seem much cleaner than knowing what it does.
           */
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index ffbd3ee..1103984 100644
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
*************** static Node *process_sublinks_mutator(No
*** 79,84 ****
--- 79,85 ----
                           process_sublinks_context *context);
  static Bitmapset *finalize_plan(PlannerInfo *root,
                Plan *plan,
+               int gather_param,
                Bitmapset *valid_params,
                Bitmapset *scan_params);
  static bool finalize_primnode(Node *node, finalize_primnode_context *context);
*************** void
*** 2217,2228 ****
  SS_finalize_plan(PlannerInfo *root, Plan *plan)
  {
      /* No setup needed, just recurse through plan tree. */
!     (void) finalize_plan(root, plan, root->outer_params, NULL);
  }

  /*
   * Recursive processing of all nodes in the plan tree
   *
   * valid_params is the set of param IDs supplied by outer plan levels
   * that are valid to reference in this plan node or its children.
   *
--- 2218,2232 ----
  SS_finalize_plan(PlannerInfo *root, Plan *plan)
  {
      /* No setup needed, just recurse through plan tree. */
!     (void) finalize_plan(root, plan, -1, root->outer_params, NULL);
  }

  /*
   * Recursive processing of all nodes in the plan tree
   *
+  * gather_param is the rescan_param of an ancestral Gather/GatherMerge,
+  * or -1 if there is none.
+  *
   * valid_params is the set of param IDs supplied by outer plan levels
   * that are valid to reference in this plan node or its children.
   *
*************** SS_finalize_plan(PlannerInfo *root, Plan
*** 2249,2255 ****
   * can be handled more cleanly.
   */
  static Bitmapset *
! finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
                Bitmapset *scan_params)
  {
      finalize_primnode_context context;
--- 2253,2261 ----
   * can be handled more cleanly.
   */
  static Bitmapset *
! finalize_plan(PlannerInfo *root, Plan *plan,
!               int gather_param,
!               Bitmapset *valid_params,
                Bitmapset *scan_params)
  {
      finalize_primnode_context context;
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2302,2307 ****
--- 2308,2325 ----
      finalize_primnode((Node *) plan->targetlist, &context);
      finalize_primnode((Node *) plan->qual, &context);

+     /*
+      * If it's a parallel-aware scan node, mark it as dependent on the parent
+      * Gather/GatherMerge's rescan Param.
+      */
+     if (plan->parallel_aware)
+     {
+         if (gather_param < 0)
+             elog(ERROR, "parallel-aware plan node is not below a Gather");
+         context.paramids =
+             bms_add_member(context.paramids, gather_param);
+     }
+
      /* Check additional node-type-specific fields */
      switch (nodeTag(plan))
      {
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2512,2517 ****
--- 2530,2536 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(lc),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2542,2547 ****
--- 2561,2567 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(l),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2558,2563 ****
--- 2578,2584 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(l),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2574,2579 ****
--- 2595,2601 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(l),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2590,2595 ****
--- 2612,2618 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(l),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2606,2611 ****
--- 2629,2635 ----
                          bms_add_members(context.paramids,
                                          finalize_plan(root,
                                                        (Plan *) lfirst(l),
+                                                       gather_param,
                                                        valid_params,
                                                        scan_params));
                  }
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2697,2709 ****
                                &context);
              break;

          case T_ProjectSet:
          case T_Hash:
          case T_Material:
          case T_Sort:
          case T_Unique:
-         case T_Gather:
-         case T_GatherMerge:
          case T_SetOp:
          case T_Group:
              /* no node-type-specific fields need fixing */
--- 2721,2771 ----
                                &context);
              break;

+         case T_Gather:
+             /* child nodes are allowed to reference rescan_param, if any */
+             locally_added_param = ((Gather *) plan)->rescan_param;
+             if (locally_added_param >= 0)
+             {
+                 valid_params = bms_add_member(bms_copy(valid_params),
+                                               locally_added_param);
+
+                 /*
+                  * We currently don't support nested Gathers.  The issue so
+                  * far as this function is concerned would be how to identify
+                  * which child nodes depend on which Gather.
+                  */
+                 Assert(gather_param < 0);
+                 /* Pass down rescan_param to child parallel-aware nodes */
+                 gather_param = locally_added_param;
+             }
+             /* rescan_param does *not* get added to scan_params */
+             break;
+
+         case T_GatherMerge:
+             /* child nodes are allowed to reference rescan_param, if any */
+             locally_added_param = ((GatherMerge *) plan)->rescan_param;
+             if (locally_added_param >= 0)
+             {
+                 valid_params = bms_add_member(bms_copy(valid_params),
+                                               locally_added_param);
+
+                 /*
+                  * We currently don't support nested Gathers.  The issue so
+                  * far as this function is concerned would be how to identify
+                  * which child nodes depend on which Gather.
+                  */
+                 Assert(gather_param < 0);
+                 /* Pass down rescan_param to child parallel-aware nodes */
+                 gather_param = locally_added_param;
+             }
+             /* rescan_param does *not* get added to scan_params */
+             break;
+
          case T_ProjectSet:
          case T_Hash:
          case T_Material:
          case T_Sort:
          case T_Unique:
          case T_SetOp:
          case T_Group:
              /* no node-type-specific fields need fixing */
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2717,2722 ****
--- 2779,2785 ----
      /* Process left and right child plans, if any */
      child_params = finalize_plan(root,
                                   plan->lefttree,
+                                  gather_param,
                                   valid_params,
                                   scan_params);
      context.paramids = bms_add_members(context.paramids, child_params);
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2726,2731 ****
--- 2789,2795 ----
          /* right child can reference nestloop_params as well as valid_params */
          child_params = finalize_plan(root,
                                       plan->righttree,
+                                      gather_param,
                                       bms_union(nestloop_params, valid_params),
                                       scan_params);
          /* ... and they don't count as parameters used at my level */
*************** finalize_plan(PlannerInfo *root, Plan *p
*** 2737,2742 ****
--- 2801,2807 ----
          /* easy case */
          child_params = finalize_plan(root,
                                       plan->righttree,
+                                      gather_param,
                                       valid_params,
                                       scan_params);
      }
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c51e7f..a382331 100644
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
*************** typedef struct Unique
*** 825,837 ****

  /* ------------
   *        gather node
   * ------------
   */
  typedef struct Gather
  {
      Plan        plan;
!     int            num_workers;
!     bool        single_copy;
      bool        invisible;        /* suppress EXPLAIN display (for testing)? */
  } Gather;

--- 825,845 ----

  /* ------------
   *        gather node
+  *
+  * Note: rescan_param is the ID of a PARAM_EXEC parameter slot.  That slot
+  * will never actually contain a value, but the Gather node must flag it as
+  * having changed whenever it is rescanned.  The child parallel-aware scan
+  * nodes are marked as depending on that parameter, so that the rescan
+  * machinery is aware that their output is likely to change across rescans.
+  * In some cases we don't need a rescan Param, so rescan_param is set to -1.
   * ------------
   */
  typedef struct Gather
  {
      Plan        plan;
!     int            num_workers;    /* planned number of worker processes */
!     int            rescan_param;    /* ID of Param that signals a rescan, or -1 */
!     bool        single_copy;    /* don't execute plan more than once */
      bool        invisible;        /* suppress EXPLAIN display (for testing)? */
  } Gather;

*************** typedef struct Gather
*** 842,848 ****
  typedef struct GatherMerge
  {
      Plan        plan;
!     int            num_workers;
      /* remaining fields are just like the sort-key info in struct Sort */
      int            numCols;        /* number of sort-key columns */
      AttrNumber *sortColIdx;        /* their indexes in the target list */
--- 850,857 ----
  typedef struct GatherMerge
  {
      Plan        plan;
!     int            num_workers;    /* planned number of worker processes */
!     int            rescan_param;    /* ID of Param that signals a rescan, or -1 */
      /* remaining fields are just like the sort-key info in struct Sort */
      int            numCols;        /* number of sort-key columns */
      AttrNumber *sortColIdx;        /* their indexes in the target list */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3ccc9d1..bd27088 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct GatherPath
*** 1268,1276 ****
  } GatherPath;

  /*
!  * GatherMergePath runs several copies of a plan in parallel and
!  * collects the results. For gather merge parallel leader always execute the
!  * plan.
   */
  typedef struct GatherMergePath
  {
--- 1268,1276 ----
  } GatherPath;

  /*
!  * GatherMergePath runs several copies of a plan in parallel and collects
!  * the results, preserving their common sort order. For gather merge, the
!  * parallel leader always executes the plan too, so we don't need single_copy.
   */
  typedef struct GatherMergePath
  {
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 084f0f0..c5bb500 100644
*** a/src/test/regress/expected/select_parallel.out
--- b/src/test/regress/expected/select_parallel.out
*************** select count(*) from tenk1 group by twen
*** 300,305 ****
--- 300,348 ----
     500
  (20 rows)

+ --test rescan behavior of gather merge
+ set enable_material = false;
+ explain (costs off)
+ select * from
+   (select string4, count(unique2)
+    from tenk1 group by string4 order by string4) ss
+   right join (values (1),(2),(3)) v(x) on true;
+                         QUERY PLAN
+ ----------------------------------------------------------
+  Nested Loop Left Join
+    ->  Values Scan on "*VALUES*"
+    ->  Finalize GroupAggregate
+          Group Key: tenk1.string4
+          ->  Gather Merge
+                Workers Planned: 4
+                ->  Partial GroupAggregate
+                      Group Key: tenk1.string4
+                      ->  Sort
+                            Sort Key: tenk1.string4
+                            ->  Parallel Seq Scan on tenk1
+ (11 rows)
+
+ select * from
+   (select string4, count(unique2)
+    from tenk1 group by string4 order by string4) ss
+   right join (values (1),(2),(3)) v(x) on true;
+  string4 | count | x
+ ---------+-------+---
+  AAAAxx  |  2500 | 1
+  HHHHxx  |  2500 | 1
+  OOOOxx  |  2500 | 1
+  VVVVxx  |  2500 | 1
+  AAAAxx  |  2500 | 2
+  HHHHxx  |  2500 | 2
+  OOOOxx  |  2500 | 2
+  VVVVxx  |  2500 | 2
+  AAAAxx  |  2500 | 3
+  HHHHxx  |  2500 | 3
+  OOOOxx  |  2500 | 3
+  VVVVxx  |  2500 | 3
+ (12 rows)
+
+ reset enable_material;
  -- gather merge test with 0 worker
  set max_parallel_workers = 0;
  explain (costs off)
diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql
index 58c3f59..bb322b0 100644
*** a/src/test/regress/sql/select_parallel.sql
--- b/src/test/regress/sql/select_parallel.sql
*************** explain (costs off)
*** 118,123 ****
--- 118,139 ----

  select count(*) from tenk1 group by twenty;

+ --test rescan behavior of gather merge
+ set enable_material = false;
+
+ explain (costs off)
+ select * from
+   (select string4, count(unique2)
+    from tenk1 group by string4 order by string4) ss
+   right join (values (1),(2),(3)) v(x) on true;
+
+ select * from
+   (select string4, count(unique2)
+    from tenk1 group by string4 order by string4) ss
+   right join (values (1),(2),(3)) v(x) on true;
+
+ reset enable_material;
+
  -- gather merge test with 0 worker
  set max_parallel_workers = 0;
  explain (costs off)

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: [HACKERS] [BUGS] [postgresql 10 beta3] unrecognized node type: 90
Следующее
От: Tatsuo Ishii
Дата:
Сообщение: Re: [HACKERS] pgbench: faster version of tpcb-like transaction