Re: explain.c: why trace PlanState and Plan trees separately?

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: explain.c: why trace PlanState and Plan trees separately?
Дата
Msg-id 3875.1279044865@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: explain.c: why trace PlanState and Plan trees separately?  (Yeb Havinga <yebhavinga@gmail.com>)
Список pgsql-hackers
Yeb Havinga <yebhavinga@gmail.com> writes:
> Will the new referenced expression printing also be used when printing
> subplans?

> If yes, I do not have to submit the latest version of a patch I made for
> subplan argument printing (discussed earlier in this thread
> http://archives.postgresql.org/pgsql-hackers/2010-02/msg01602.php)

Oh, I had forgotten that patch was still in progress.  I think it's
unnecessary given what I'm fooling with.  The attached patch needs
some more testing, but what I get with it is for example

regression=# explain (verbose) select (select oid from pg_class a where
regression(# a.oid = b.relfilenode) from pg_class b;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Seq Scan on pg_catalog.pg_class b  (cost=0.00..5556.81 rows=669 width=4)
   Output: (SubPlan 1)
   SubPlan 1
     ->  Index Scan using pg_class_oid_index on pg_catalog.pg_class a  (cost=0.00..8.27 rows=1 width=4)
           Output: a.oid
           Index Cond: (a.oid = b.relfilenode)
(6 rows)

(this is the first example in the above-referenced thread).

            regards, tom lane

Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.206
diff -c -r1.206 explain.c
*** src/backend/commands/explain.c    10 Jun 2010 01:26:30 -0000    1.206
--- src/backend/commands/explain.c    13 Jul 2010 18:13:25 -0000
***************
*** 54,80 ****
  static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                  ExplainState *es);
  static double elapsed_time(instr_time *starttime);
! static void ExplainNode(Plan *plan, PlanState *planstate,
!             Plan *outer_plan,
              const char *relationship, const char *plan_name,
              ExplainState *es);
! static void show_plan_tlist(Plan *plan, ExplainState *es);
! static void show_qual(List *qual, const char *qlabel, Plan *plan,
!           Plan *outer_plan, bool useprefix, ExplainState *es);
  static void show_scan_qual(List *qual, const char *qlabel,
!                Plan *scan_plan, Plan *outer_plan,
!                ExplainState *es);
! static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
!                 ExplainState *es);
! static void show_sort_keys(Plan *sortplan, ExplainState *es);
  static void show_sort_info(SortState *sortstate, ExplainState *es);
  static void show_hash_info(HashState *hashstate, ExplainState *es);
  static const char *explain_get_index_name(Oid indexId);
  static void ExplainScanTarget(Scan *plan, ExplainState *es);
! static void ExplainMemberNodes(List *plans, PlanState **planstate,
!                    Plan *outer_plan, ExplainState *es);
! static void ExplainSubPlans(List *plans, const char *relationship,
!                 ExplainState *es);
  static void ExplainPropertyList(const char *qlabel, List *data,
                      ExplainState *es);
  static void ExplainProperty(const char *qlabel, const char *value,
--- 54,83 ----
  static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                  ExplainState *es);
  static double elapsed_time(instr_time *starttime);
! static void ExplainNode(PlanState *planstate, List *ancestors,
              const char *relationship, const char *plan_name,
              ExplainState *es);
! static void show_plan_tlist(PlanState *planstate, List *ancestors,
!                             ExplainState *es);
! static void show_qual(List *qual, const char *qlabel,
!                       PlanState *planstate, List *ancestors,
!                       bool useprefix, ExplainState *es);
  static void show_scan_qual(List *qual, const char *qlabel,
!                            PlanState *planstate, List *ancestors,
!                            ExplainState *es);
! static void show_upper_qual(List *qual, const char *qlabel,
!                             PlanState *planstate, List *ancestors,
!                             ExplainState *es);
! static void show_sort_keys(SortState *sortstate, List *ancestors,
!                            ExplainState *es);
  static void show_sort_info(SortState *sortstate, ExplainState *es);
  static void show_hash_info(HashState *hashstate, ExplainState *es);
  static const char *explain_get_index_name(Oid indexId);
  static void ExplainScanTarget(Scan *plan, ExplainState *es);
! static void ExplainMemberNodes(List *plans, PlanState **planstates,
!                    List *ancestors, ExplainState *es);
! static void ExplainSubPlans(List *plans, List *ancestors,
!                             const char *relationship, ExplainState *es);
  static void ExplainPropertyList(const char *qlabel, List *data,
                      ExplainState *es);
  static void ExplainProperty(const char *qlabel, const char *value,
***************
*** 484,491 ****
      Assert(queryDesc->plannedstmt != NULL);
      es->pstmt = queryDesc->plannedstmt;
      es->rtable = queryDesc->plannedstmt->rtable;
!     ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
!                 NULL, NULL, NULL, es);
  }

  /*
--- 487,493 ----
      Assert(queryDesc->plannedstmt != NULL);
      es->pstmt = queryDesc->plannedstmt;
      es->rtable = queryDesc->plannedstmt->rtable;
!     ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
  }

  /*
***************
*** 585,615 ****

  /*
   * ExplainNode -
!  *      Appends a description of the Plan node to es->str
   *
!  * planstate points to the executor state node corresponding to the plan node.
!  * We need this to get at the instrumentation data (if any) as well as the
!  * list of subplans.
!  *
!  * outer_plan, if not null, references another plan node that is the outer
!  * side of a join with the current node.  This is only interesting for
!  * deciphering runtime keys of an inner indexscan.
   *
   * relationship describes the relationship of this plan node to its parent
   * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
   * optional name to be attached to the node.
   *
   * In text format, es->indent is controlled in this function since we only
!  * want it to change at Plan-node boundaries.  In non-text formats, es->indent
   * corresponds to the nesting depth of logical output groups, and therefore
   * is controlled by ExplainOpenGroup/ExplainCloseGroup.
   */
  static void
! ExplainNode(Plan *plan, PlanState *planstate,
!             Plan *outer_plan,
              const char *relationship, const char *plan_name,
              ExplainState *es)
  {
      const char *pname;            /* node type name for text output */
      const char *sname;            /* node type name for non-text output */
      const char *strategy = NULL;
--- 587,616 ----

  /*
   * ExplainNode -
!  *      Appends a description of a plan tree to es->str
   *
!  * planstate points to the executor state node for the current plan node.
!  * We need to work from a PlanState node, not just a Plan node, in order to
!  * get at the instrumentation data (if any) as well as the list of subplans.
!  *
!  * ancestors is a list of parent PlanState nodes, most-closely-nested first.
!  * These are needed in order to interpret PARAM_EXEC Params.
   *
   * relationship describes the relationship of this plan node to its parent
   * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
   * optional name to be attached to the node.
   *
   * In text format, es->indent is controlled in this function since we only
!  * want it to change at plan-node boundaries.  In non-text formats, es->indent
   * corresponds to the nesting depth of logical output groups, and therefore
   * is controlled by ExplainOpenGroup/ExplainCloseGroup.
   */
  static void
! ExplainNode(PlanState *planstate, List *ancestors,
              const char *relationship, const char *plan_name,
              ExplainState *es)
  {
+     Plan       *plan = planstate->plan;
      const char *pname;            /* node type name for text output */
      const char *sname;            /* node type name for non-text output */
      const char *strategy = NULL;
***************
*** 617,624 ****
      int            save_indent = es->indent;
      bool        haschildren;

-     Assert(plan);
-
      switch (nodeTag(plan))
      {
          case T_Result:
--- 618,623 ----
***************
*** 999,1021 ****

      /* target list */
      if (es->verbose)
!         show_plan_tlist(plan, es);

      /* quals, sort keys, etc */
      switch (nodeTag(plan))
      {
          case T_IndexScan:
              show_scan_qual(((IndexScan *) plan)->indexqualorig,
!                            "Index Cond", plan, outer_plan, es);
!             show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
              break;
          case T_BitmapIndexScan:
              show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
!                            "Index Cond", plan, outer_plan, es);
              break;
          case T_BitmapHeapScan:
              show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
!                            "Recheck Cond", plan, outer_plan, es);
              /* FALL THRU */
          case T_SeqScan:
          case T_FunctionScan:
--- 998,1020 ----

      /* target list */
      if (es->verbose)
!         show_plan_tlist(planstate, ancestors, es);

      /* quals, sort keys, etc */
      switch (nodeTag(plan))
      {
          case T_IndexScan:
              show_scan_qual(((IndexScan *) plan)->indexqualorig,
!                            "Index Cond", planstate, ancestors, es);
!             show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_BitmapIndexScan:
              show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
!                            "Index Cond", planstate, ancestors, es);
              break;
          case T_BitmapHeapScan:
              show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
!                            "Recheck Cond", planstate, ancestors, es);
              /* FALL THRU */
          case T_SeqScan:
          case T_FunctionScan:
***************
*** 1023,1029 ****
          case T_CteScan:
          case T_WorkTableScan:
          case T_SubqueryScan:
!             show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
              break;
          case T_TidScan:
              {
--- 1022,1028 ----
          case T_CteScan:
          case T_WorkTableScan:
          case T_SubqueryScan:
!             show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_TidScan:
              {
***************
*** 1035,1075 ****

                  if (list_length(tidquals) > 1)
                      tidquals = list_make1(make_orclause(tidquals));
!                 show_scan_qual(tidquals, "TID Cond", plan, outer_plan, es);
!                 show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
              }
              break;
          case T_NestLoop:
              show_upper_qual(((NestLoop *) plan)->join.joinqual,
!                             "Join Filter", plan, es);
!             show_upper_qual(plan->qual, "Filter", plan, es);
              break;
          case T_MergeJoin:
              show_upper_qual(((MergeJoin *) plan)->mergeclauses,
!                             "Merge Cond", plan, es);
              show_upper_qual(((MergeJoin *) plan)->join.joinqual,
!                             "Join Filter", plan, es);
!             show_upper_qual(plan->qual, "Filter", plan, es);
              break;
          case T_HashJoin:
              show_upper_qual(((HashJoin *) plan)->hashclauses,
!                             "Hash Cond", plan, es);
              show_upper_qual(((HashJoin *) plan)->join.joinqual,
!                             "Join Filter", plan, es);
!             show_upper_qual(plan->qual, "Filter", plan, es);
              break;
          case T_Agg:
          case T_Group:
!             show_upper_qual(plan->qual, "Filter", plan, es);
              break;
          case T_Sort:
!             show_sort_keys(plan, es);
              show_sort_info((SortState *) planstate, es);
              break;
          case T_Result:
              show_upper_qual((List *) ((Result *) plan)->resconstantqual,
!                             "One-Time Filter", plan, es);
!             show_upper_qual(plan->qual, "Filter", plan, es);
              break;
          case T_Hash:
              show_hash_info((HashState *) planstate, es);
--- 1034,1074 ----

                  if (list_length(tidquals) > 1)
                      tidquals = list_make1(make_orclause(tidquals));
!                 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
!                 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
              }
              break;
          case T_NestLoop:
              show_upper_qual(((NestLoop *) plan)->join.joinqual,
!                             "Join Filter", planstate, ancestors, es);
!             show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_MergeJoin:
              show_upper_qual(((MergeJoin *) plan)->mergeclauses,
!                             "Merge Cond", planstate, ancestors, es);
              show_upper_qual(((MergeJoin *) plan)->join.joinqual,
!                             "Join Filter", planstate, ancestors, es);
!             show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_HashJoin:
              show_upper_qual(((HashJoin *) plan)->hashclauses,
!                             "Hash Cond", planstate, ancestors, es);
              show_upper_qual(((HashJoin *) plan)->join.joinqual,
!                             "Join Filter", planstate, ancestors, es);
!             show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_Agg:
          case T_Group:
!             show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_Sort:
!             show_sort_keys((SortState *) planstate, ancestors, es);
              show_sort_info((SortState *) planstate, es);
              break;
          case T_Result:
              show_upper_qual((List *) ((Result *) plan)->resconstantqual,
!                             "One-Time Filter", planstate, ancestors, es);
!             show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
              break;
          case T_Hash:
              show_hash_info((HashState *) planstate, es);
***************
*** 1169,1198 ****
      if (haschildren)
          ExplainOpenGroup("Plans", "Plans", false, es);

      /* initPlan-s */
      if (plan->initPlan)
!         ExplainSubPlans(planstate->initPlan, "InitPlan", es);

      /* lefttree */
!     if (outerPlan(plan))
!     {
!         /*
!          * Ordinarily we don't pass down our own outer_plan value to our child
!          * nodes, but in bitmap scan trees we must, since the bottom
!          * BitmapIndexScan nodes may have outer references.
!          */
!         ExplainNode(outerPlan(plan), outerPlanState(planstate),
!                     IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
                      "Outer", NULL, es);
-     }

      /* righttree */
!     if (innerPlan(plan))
!     {
!         ExplainNode(innerPlan(plan), innerPlanState(planstate),
!                     outerPlan(plan),
                      "Inner", NULL, es);
-     }

      /* special child plans */
      switch (nodeTag(plan))
--- 1168,1189 ----
      if (haschildren)
          ExplainOpenGroup("Plans", "Plans", false, es);

+     /* Pass current PlanState as head of ancestors list for children */
+     ancestors = lcons(planstate, ancestors);
+
      /* initPlan-s */
      if (plan->initPlan)
!         ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);

      /* lefttree */
!     if (outerPlanState(planstate))
!         ExplainNode(outerPlanState(planstate), ancestors,
                      "Outer", NULL, es);

      /* righttree */
!     if (innerPlanState(planstate))
!         ExplainNode(innerPlanState(planstate), ancestors,
                      "Inner", NULL, es);

      /* special child plans */
      switch (nodeTag(plan))
***************
*** 1200,1231 ****
          case T_ModifyTable:
              ExplainMemberNodes(((ModifyTable *) plan)->plans,
                                 ((ModifyTableState *) planstate)->mt_plans,
!                                outer_plan, es);
              break;
          case T_Append:
              ExplainMemberNodes(((Append *) plan)->appendplans,
                                 ((AppendState *) planstate)->appendplans,
!                                outer_plan, es);
              break;
          case T_BitmapAnd:
              ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
                                 ((BitmapAndState *) planstate)->bitmapplans,
!                                outer_plan, es);
              break;
          case T_BitmapOr:
              ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
                                 ((BitmapOrState *) planstate)->bitmapplans,
!                                outer_plan, es);
              break;
          case T_SubqueryScan:
!             {
!                 SubqueryScan *subqueryscan = (SubqueryScan *) plan;
!                 SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
!
!                 ExplainNode(subqueryscan->subplan, subquerystate->subplan,
!                             NULL,
!                             "Subquery", NULL, es);
!             }
              break;
          default:
              break;
--- 1191,1216 ----
          case T_ModifyTable:
              ExplainMemberNodes(((ModifyTable *) plan)->plans,
                                 ((ModifyTableState *) planstate)->mt_plans,
!                                ancestors, es);
              break;
          case T_Append:
              ExplainMemberNodes(((Append *) plan)->appendplans,
                                 ((AppendState *) planstate)->appendplans,
!                                ancestors, es);
              break;
          case T_BitmapAnd:
              ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
                                 ((BitmapAndState *) planstate)->bitmapplans,
!                                ancestors, es);
              break;
          case T_BitmapOr:
              ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
                                 ((BitmapOrState *) planstate)->bitmapplans,
!                                ancestors, es);
              break;
          case T_SubqueryScan:
!             ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
!                         "Subquery", NULL, es);
              break;
          default:
              break;
***************
*** 1233,1241 ****

      /* subPlan-s */
      if (planstate->subPlan)
!         ExplainSubPlans(planstate->subPlan, "SubPlan", es);

      /* end of child plans */
      if (haschildren)
          ExplainCloseGroup("Plans", "Plans", false, es);

--- 1218,1227 ----

      /* subPlan-s */
      if (planstate->subPlan)
!         ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);

      /* end of child plans */
+     ancestors = list_delete_first(ancestors);
      if (haschildren)
          ExplainCloseGroup("Plans", "Plans", false, es);

***************
*** 1252,1259 ****
   * Show the targetlist of a plan node
   */
  static void
! show_plan_tlist(Plan *plan, ExplainState *es)
  {
      List       *context;
      List       *result = NIL;
      bool        useprefix;
--- 1238,1246 ----
   * Show the targetlist of a plan node
   */
  static void
! show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
  {
+     Plan       *plan = planstate->plan;
      List       *context;
      List       *result = NIL;
      bool        useprefix;
***************
*** 1271,1280 ****
          return;

      /* Set up deparsing context */
!     context = deparse_context_for_plan((Node *) plan,
!                                        NULL,
!                                        es->rtable,
!                                        es->pstmt->subplans);
      useprefix = list_length(es->rtable) > 1;

      /* Deparse each result column (we now include resjunk ones) */
--- 1258,1266 ----
          return;

      /* Set up deparsing context */
!     context = deparse_context_for_planstate((Node *) planstate,
!                                             ancestors,
!                                             es->rtable);
      useprefix = list_length(es->rtable) > 1;

      /* Deparse each result column (we now include resjunk ones) */
***************
*** 1294,1305 ****

  /*
   * Show a qualifier expression
-  *
-  * Note: outer_plan is the referent for any OUTER vars in the scan qual;
-  * this would be the outer side of a nestloop plan.  Pass NULL if none.
   */
  static void
! show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
            bool useprefix, ExplainState *es)
  {
      List       *context;
--- 1280,1289 ----

  /*
   * Show a qualifier expression
   */
  static void
! show_qual(List *qual, const char *qlabel,
!           PlanState *planstate, List *ancestors,
            bool useprefix, ExplainState *es)
  {
      List       *context;
***************
*** 1314,1323 ****
      node = (Node *) make_ands_explicit(qual);

      /* Set up deparsing context */
!     context = deparse_context_for_plan((Node *) plan,
!                                        (Node *) outer_plan,
!                                        es->rtable,
!                                        es->pstmt->subplans);

      /* Deparse the expression */
      exprstr = deparse_expression(node, context, useprefix, false);
--- 1298,1306 ----
      node = (Node *) make_ands_explicit(qual);

      /* Set up deparsing context */
!     context = deparse_context_for_planstate((Node *) planstate,
!                                             ancestors,
!                                             es->rtable);

      /* Deparse the expression */
      exprstr = deparse_expression(node, context, useprefix, false);
***************
*** 1331,1366 ****
   */
  static void
  show_scan_qual(List *qual, const char *qlabel,
!                Plan *scan_plan, Plan *outer_plan,
                 ExplainState *es)
  {
      bool        useprefix;

!     useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
!                  es->verbose);
!     show_qual(qual, qlabel, scan_plan, outer_plan, useprefix, es);
  }

  /*
   * Show a qualifier expression for an upper-level plan node
   */
  static void
! show_upper_qual(List *qual, const char *qlabel, Plan *plan, ExplainState *es)
  {
      bool        useprefix;

      useprefix = (list_length(es->rtable) > 1 || es->verbose);
!     show_qual(qual, qlabel, plan, NULL, useprefix, es);
  }

  /*
   * Show the sort keys for a Sort node.
   */
  static void
! show_sort_keys(Plan *sortplan, ExplainState *es)
  {
!     int            nkeys = ((Sort *) sortplan)->numCols;
!     AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
      List       *context;
      List       *result = NIL;
      bool        useprefix;
--- 1314,1351 ----
   */
  static void
  show_scan_qual(List *qual, const char *qlabel,
!                PlanState *planstate, List *ancestors,
                 ExplainState *es)
  {
      bool        useprefix;

!     useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
!     show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
  }

  /*
   * Show a qualifier expression for an upper-level plan node
   */
  static void
! show_upper_qual(List *qual, const char *qlabel,
!                 PlanState *planstate, List *ancestors,
!                 ExplainState *es)
  {
      bool        useprefix;

      useprefix = (list_length(es->rtable) > 1 || es->verbose);
!     show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
  }

  /*
   * Show the sort keys for a Sort node.
   */
  static void
! show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
  {
!     Sort       *plan = (Sort *) sortstate->ss.ps.plan;
!     int            nkeys = plan->numCols;
!     AttrNumber *keycols = plan->sortColIdx;
      List       *context;
      List       *result = NIL;
      bool        useprefix;
***************
*** 1371,1387 ****
          return;

      /* Set up deparsing context */
!     context = deparse_context_for_plan((Node *) sortplan,
!                                        NULL,
!                                        es->rtable,
!                                        es->pstmt->subplans);
      useprefix = (list_length(es->rtable) > 1 || es->verbose);

      for (keyno = 0; keyno < nkeys; keyno++)
      {
          /* find key expression in tlist */
          AttrNumber    keyresno = keycols[keyno];
!         TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);

          if (!target)
              elog(ERROR, "no tlist entry for key %d", keyresno);
--- 1356,1372 ----
          return;

      /* Set up deparsing context */
!     context = deparse_context_for_planstate((Node *) sortstate,
!                                             ancestors,
!                                             es->rtable);
      useprefix = (list_length(es->rtable) > 1 || es->verbose);

      for (keyno = 0; keyno < nkeys; keyno++)
      {
          /* find key expression in tlist */
          AttrNumber    keyresno = keycols[keyno];
!         TargetEntry *target = get_tle_by_resno(plan->plan.targetlist,
!                                                keyresno);

          if (!target)
              elog(ERROR, "no tlist entry for key %d", keyresno);
***************
*** 1596,1629 ****
   * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
   * or BitmapOr node.
   *
!  * Ordinarily we don't pass down outer_plan to our child nodes, but in these
!  * cases we must, since the node could be an "inner indexscan" in which case
!  * outer references can appear in the child nodes.
   */
  static void
! ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
!                    ExplainState *es)
  {
!     ListCell   *lst;
!     int            j = 0;

!     foreach(lst, plans)
!     {
!         Plan       *subnode = (Plan *) lfirst(lst);
!
!         ExplainNode(subnode, planstate[j],
!                     outer_plan,
!                     "Member", NULL,
!                     es);
!         j++;
!     }
  }

  /*
   * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
   */
  static void
! ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
  {
      ListCell   *lst;

--- 1581,1613 ----
   * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
   * or BitmapOr node.
   *
!  * The ancestors list should already contain the immediate parent of these
!  * plans.
!  *
!  * Note: we don't actually need to examine the Plan list members, but
!  * we need the list in order to determine the length of the PlanState array.
   */
  static void
! ExplainMemberNodes(List *plans, PlanState **planstates,
!                    List *ancestors, ExplainState *es)
  {
!     int            nplans = list_length(plans);
!     int            j;

!     for (j = 0; j < nplans; j++)
!         ExplainNode(planstates[j], ancestors,
!                     "Member", NULL, es);
  }

  /*
   * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
+  *
+  * The ancestors list should already contain the immediate parent of these
+  * SubPlanStates.
   */
  static void
! ExplainSubPlans(List *plans, List *ancestors,
!                 const char *relationship, ExplainState *es)
  {
      ListCell   *lst;

***************
*** 1632,1642 ****
          SubPlanState *sps = (SubPlanState *) lfirst(lst);
          SubPlan    *sp = (SubPlan *) sps->xprstate.expr;

!         ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
!                     sps->planstate,
!                     NULL,
!                     relationship, sp->plan_name,
!                     es);
      }
  }

--- 1616,1623 ----
          SubPlanState *sps = (SubPlanState *) lfirst(lst);
          SubPlan    *sp = (SubPlan *) sps->xprstate.expr;

!         ExplainNode(sps->planstate, ancestors,
!                     relationship, sp->plan_name, es);
      }
  }

Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.327
diff -c -r1.327 ruleutils.c
*** src/backend/utils/adt/ruleutils.c    9 Jul 2010 21:11:47 -0000    1.327
--- src/backend/utils/adt/ruleutils.c    13 Jul 2010 18:13:25 -0000
***************
*** 102,117 ****
   * The rangetable is the list of actual RTEs from the query tree, and the
   * cte list is the list of actual CTEs.
   *
!  * For deparsing plan trees, we provide for outer and inner subplan nodes.
!  * The tlists of these nodes are used to resolve OUTER and INNER varnos.
!  * Also, in the plan-tree case we don't have access to the parse-time CTE
!  * list, so we need a list of subplans instead.
   */
  typedef struct
  {
      List       *rtable;            /* List of RangeTblEntry nodes */
      List       *ctes;            /* List of CommonTableExpr nodes */
!     List       *subplans;        /* List of subplans, in plan-tree case */
      Plan       *outer_plan;        /* OUTER subplan, or NULL if none */
      Plan       *inner_plan;        /* INNER subplan, or NULL if none */
  } deparse_namespace;
--- 102,124 ----
   * The rangetable is the list of actual RTEs from the query tree, and the
   * cte list is the list of actual CTEs.
   *
!  * When deparsing plan trees, there is always just a single item in the
!  * deparse_namespace list (since a plan tree never contains Vars with
!  * varlevelsup > 0).  We store the PlanState node that is the immediate
!  * parent of the expression to be deparsed, as well as a list of that
!  * PlanState's ancestors.  In addition, we store the outer and inner
!  * subplan nodes, whose targetlists are used to resolve OUTER and INNER Vars.
!  * (Note: these could be derived on-the-fly from the planstate instead.)
   */
  typedef struct
  {
      List       *rtable;            /* List of RangeTblEntry nodes */
      List       *ctes;            /* List of CommonTableExpr nodes */
!     /* Remaining fields are used only when deparsing a Plan tree: */
!     PlanState  *planstate;        /* immediate parent of current expression */
!     List       *ancestors;        /* ancestors of planstate */
!     PlanState  *outer_planstate;    /* OUTER subplan state, or NULL if none */
!     PlanState  *inner_planstate;    /* INNER subplan state, or NULL if none */
      Plan       *outer_plan;        /* OUTER subplan, or NULL if none */
      Plan       *inner_plan;        /* INNER subplan, or NULL if none */
  } deparse_namespace;
***************
*** 154,159 ****
--- 161,175 ----
  static int print_function_arguments(StringInfo buf, HeapTuple proctup,
                           bool print_table_args, bool print_defaults);
  static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+ static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
+ static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
+                 deparse_namespace *save_dpns);
+ static void pop_child_plan(deparse_namespace *dpns,
+                            deparse_namespace *save_dpns);
+ static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+                    deparse_namespace *save_dpns);
+ static void pop_ancestor_plan(deparse_namespace *dpns,
+                               deparse_namespace *save_dpns);
  static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
               int prettyFlags);
  static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
***************
*** 183,193 ****
  static void get_rule_windowclause(Query *query, deparse_context *context);
  static void get_rule_windowspec(WindowClause *wc, List *targetList,
                      deparse_context *context);
- static void push_plan(deparse_namespace *dpns, Plan *subplan);
  static char *get_variable(Var *var, int levelsup, bool showstar,
               deparse_context *context);
  static RangeTblEntry *find_rte_by_refname(const char *refname,
                      deparse_context *context);
  static const char *get_simple_binary_op_name(OpExpr *expr);
  static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
  static void appendContextKeyword(deparse_context *context, const char *str,
--- 199,211 ----
  static void get_rule_windowclause(Query *query, deparse_context *context);
  static void get_rule_windowspec(WindowClause *wc, List *targetList,
                      deparse_context *context);
  static char *get_variable(Var *var, int levelsup, bool showstar,
               deparse_context *context);
  static RangeTblEntry *find_rte_by_refname(const char *refname,
                      deparse_context *context);
+ static void get_parameter(Param *param, deparse_context *context);
+ static void print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+                      deparse_namespace *dpns, deparse_context *context);
  static const char *get_simple_binary_op_name(OpExpr *expr);
  static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
  static void appendContextKeyword(deparse_context *context, const char *str,
***************
*** 625,634 ****
          newrte->inFromCl = true;

          /* Build two-element rtable */
          dpns.rtable = list_make2(oldrte, newrte);
          dpns.ctes = NIL;
-         dpns.subplans = NIL;
-         dpns.outer_plan = dpns.inner_plan = NULL;

          /* Set up context with one-deep namespace stack */
          context.buf = &buf;
--- 643,651 ----
          newrte->inFromCl = true;

          /* Build two-element rtable */
+         memset(&dpns, 0, sizeof(dpns));
          dpns.rtable = list_make2(oldrte, newrte);
          dpns.ctes = NIL;

          /* Set up context with one-deep namespace stack */
          context.buf = &buf;
***************
*** 2072,2078 ****
      deparse_namespace *dpns;
      RangeTblEntry *rte;

!     dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));

      /* Build a minimal RTE for the rel */
      rte = makeNode(RangeTblEntry);
--- 2089,2095 ----
      deparse_namespace *dpns;
      RangeTblEntry *rte;

!     dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));

      /* Build a minimal RTE for the rel */
      rte = makeNode(RangeTblEntry);
***************
*** 2085,2147 ****
      /* Build one-element rtable */
      dpns->rtable = list_make1(rte);
      dpns->ctes = NIL;
-     dpns->subplans = NIL;
-     dpns->outer_plan = dpns->inner_plan = NULL;

      /* Return a one-deep namespace stack */
      return list_make1(dpns);
  }

  /*
!  * deparse_context_for_plan        - Build deparse context for a plan node
   *
   * When deparsing an expression in a Plan tree, we might have to resolve
   * OUTER or INNER references.  To do this, the caller must provide the
!  * parent Plan node.  In the normal case of a join plan node, OUTER and
!  * INNER references can be resolved by drilling down into the left and
!  * right child plans.  A special case is that a nestloop inner indexscan
!  * might have OUTER Vars, but the outer side of the join is not a child
!  * plan node.  To handle such cases the outer plan node must be passed
!  * separately.    (Pass NULL for outer_plan otherwise.)
   *
!  * Note: plan and outer_plan really ought to be declared as "Plan *", but
!  * we use "Node *" to avoid having to include plannodes.h in builtins.h.
   *
   * The plan's rangetable list must also be passed.  We actually prefer to use
   * the rangetable to resolve simple Vars, but the plan inputs are necessary
   * for Vars that reference expressions computed in subplan target lists.
-  *
-  * We also need the list of subplans associated with the Plan tree; this
-  * is for resolving references to CTE subplans.
   */
  List *
! deparse_context_for_plan(Node *plan, Node *outer_plan,
!                          List *rtable, List *subplans)
  {
      deparse_namespace *dpns;

!     dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));

      dpns->rtable = rtable;
      dpns->ctes = NIL;
!     dpns->subplans = subplans;

      /*
!      * Set up outer_plan and inner_plan from the Plan node (this includes
!      * various special cases for particular Plan types).
       */
!     push_plan(dpns, (Plan *) plan);

      /*
!      * If outer_plan is given, that overrides whatever we got from the plan.
       */
-     if (outer_plan)
-         dpns->outer_plan = (Plan *) outer_plan;

!     /* Return a one-deep namespace stack */
!     return list_make1(dpns);
  }

  /* ----------
   * make_ruledef            - reconstruct the CREATE RULE command
   *                  for a given pg_rewrite tuple
--- 2102,2292 ----
      /* Build one-element rtable */
      dpns->rtable = list_make1(rte);
      dpns->ctes = NIL;

      /* Return a one-deep namespace stack */
      return list_make1(dpns);
  }

  /*
!  * deparse_context_for_planstate    - Build deparse context for a plan
   *
   * When deparsing an expression in a Plan tree, we might have to resolve
   * OUTER or INNER references.  To do this, the caller must provide the
!  * parent PlanState node.  Then OUTER and INNER references can be resolved
!  * by drilling down into the left and right child plans.
   *
!  * Note: planstate really ought to be declared as "PlanState *", but we use
!  * "Node *" to avoid having to include execnodes.h in builtins.h.
!  *
!  * The ancestors list is a list of the PlanState's parent PlanStates, the
!  * most-closely-nested first.  This is needed to resolve PARAM_EXEC Params.
!  * Note we assume that all the PlanStates share the same rtable.
   *
   * The plan's rangetable list must also be passed.  We actually prefer to use
   * the rangetable to resolve simple Vars, but the plan inputs are necessary
   * for Vars that reference expressions computed in subplan target lists.
   */
  List *
! deparse_context_for_planstate(Node *planstate, List *ancestors,
!                               List *rtable)
  {
      deparse_namespace *dpns;

!     dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));

+     /* Initialize fields that stay the same across the whole plan tree */
      dpns->rtable = rtable;
      dpns->ctes = NIL;
!
!     /* Set our attention on the specific plan node passed in */
!     set_deparse_planstate(dpns, (PlanState *) planstate);
!     dpns->ancestors = ancestors;
!
!     /* Return a one-deep namespace stack */
!     return list_make1(dpns);
! }
!
! /*
!  * set_deparse_planstate: set up deparse_namespace to parse subexpressions
!  * of a given PlanState node
!  *
!  * This sets the planstate, outer_planstate, inner_planstate, outer_plan, and
!  * inner_plan fields.  Caller is responsible for adjusting the ancestors list
!  * if necessary.  Note that the rtable and ctes fields do not need to change
!  * when shifting attention to different plan nodes in a single plan tree.
!  */
! static void
! set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
! {
!     dpns->planstate = ps;
!
!     /*
!      * We special-case Append to pretend that the first child plan is the
!      * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
!      * according to one of the children, and the first one is the most natural
!      * choice.    Likewise special-case ModifyTable to pretend that the first
!      * child plan is the OUTER referent; this is to support RETURNING lists
!      * containing references to non-target relations.
!      */
!     if (IsA(ps, AppendState))
!         dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
!     else if (IsA(ps, ModifyTableState))
!         dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
!     else
!         dpns->outer_planstate = outerPlanState(ps);
!
!     if (dpns->outer_planstate)
!         dpns->outer_plan = dpns->outer_planstate->plan;
!     else
!         dpns->outer_plan = NULL;

      /*
!      * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
!      * use OUTER because that could someday conflict with the normal meaning.)
!      * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
       */
!     if (IsA(ps, SubqueryScanState))
!         dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
!     else if (IsA(ps, CteScanState))
!         dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
!     else
!         dpns->inner_planstate = innerPlanState(ps);
!
!     if (dpns->inner_planstate)
!         dpns->inner_plan = dpns->inner_planstate->plan;
!     else
!         dpns->inner_plan = NULL;
! }
!
! /*
!  * push_child_plan: temporarily transfer deparsing attention to a child plan
!  *
!  * When expanding an OUTER or INNER reference, we must adjust the deparse
!  * context in case the referenced expression itself uses OUTER/INNER.    We
!  * modify the top stack entry in-place to avoid affecting levelsup issues
!  * (although in a Plan tree there really shouldn't be any).
!  *
!  * Caller must provide a local deparse_namespace variable to save the
!  * previous state for pop_child_plan.
!  */
! static void
! push_child_plan(deparse_namespace *dpns, PlanState *ps,
!                 deparse_namespace *save_dpns)
! {
!     /* Save state for restoration later */
!     *save_dpns = *dpns;

      /*
!      * Currently we don't bother to adjust the ancestors list, because an
!      * OUTER or INNER reference really shouldn't contain any Params that
!      * would be set by the parent node itself.  If we did want to adjust it,
!      * lcons'ing dpns->planstate onto dpns->ancestors would be the appropriate
!      * thing --- and pop_child_plan would need to undo the change to the list.
       */

!     /* Set attention on selected child */
!     set_deparse_planstate(dpns, ps);
! }
!
! /*
!  * pop_child_plan: undo the effects of push_child_plan
!  */
! static void
! pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
! {
!     /* Restore fields changed by push_child_plan */
!     *dpns = *save_dpns;
! }
!
! /*
!  * push_ancestor_plan: temporarily transfer deparsing attention to an
!  * ancestor plan
!  *
!  * When expanding a Param reference, we must adjust the deparse context
!  * to match the plan node that contains the expression being printed;
!  * otherwise we'd fail if that expression itself contains a Param or
!  * OUTER/INNER variables.
!  *
!  * The target ancestor is conveniently identified by the ListCell holding it
!  * in dpns->ancestors.
!  *
!  * Caller must provide a local deparse_namespace variable to save the
!  * previous state for pop_ancestor_plan.
!  */
! static void
! push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
!                    deparse_namespace *save_dpns)
! {
!     PlanState  *ps = (PlanState *) lfirst(ancestor_cell);
!     List       *ancestors;
!
!     /* Save state for restoration later */
!     *save_dpns = *dpns;
!
!     /* Build a new ancestor list with just this node's ancestors */
!     ancestors = NIL;
!     while ((ancestor_cell = lnext(ancestor_cell)) != NULL)
!         ancestors = lappend(ancestors, lfirst(ancestor_cell));
!     dpns->ancestors = ancestors;
!
!     /* Set attention on selected ancestor */
!     set_deparse_planstate(dpns, ps);
! }
!
! /*
!  * pop_ancestor_plan: undo the effects of push_ancestor_plan
!  */
! static void
! pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
! {
!     /* Free the ancestor list made in push_ancestor_plan */
!     list_free(dpns->ancestors);
!
!     /* Restore fields changed by push_ancestor_plan */
!     *dpns = *save_dpns;
  }

+
  /* ----------
   * make_ruledef            - reconstruct the CREATE RULE command
   *                  for a given pg_rewrite tuple
***************
*** 2285,2294 ****
          context.varprefix = (list_length(query->rtable) != 1);
          context.prettyFlags = prettyFlags;
          context.indentLevel = PRETTYINDENT_STD;
          dpns.rtable = query->rtable;
          dpns.ctes = query->cteList;
-         dpns.subplans = NIL;
-         dpns.outer_plan = dpns.inner_plan = NULL;

          get_rule_expr(qual, &context, false);
      }
--- 2430,2439 ----
          context.varprefix = (list_length(query->rtable) != 1);
          context.prettyFlags = prettyFlags;
          context.indentLevel = PRETTYINDENT_STD;
+
+         memset(&dpns, 0, sizeof(dpns));
          dpns.rtable = query->rtable;
          dpns.ctes = query->cteList;

          get_rule_expr(qual, &context, false);
      }
***************
*** 2432,2441 ****
      context.prettyFlags = prettyFlags;
      context.indentLevel = startIndent;

      dpns.rtable = query->rtable;
      dpns.ctes = query->cteList;
-     dpns.subplans = NIL;
-     dpns.outer_plan = dpns.inner_plan = NULL;

      switch (query->commandType)
      {
--- 2577,2585 ----
      context.prettyFlags = prettyFlags;
      context.indentLevel = startIndent;

+     memset(&dpns, 0, sizeof(dpns));
      dpns.rtable = query->rtable;
      dpns.ctes = query->cteList;

      switch (query->commandType)
      {
***************
*** 3481,3537 ****


  /*
-  * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
-  *
-  * When expanding an OUTER or INNER reference, we must push new outer/inner
-  * subplans in case the referenced expression itself uses OUTER/INNER.    We
-  * modify the top stack entry in-place to avoid affecting levelsup issues
-  * (although in a Plan tree there really shouldn't be any).
-  *
-  * Caller must save and restore outer_plan and inner_plan around this.
-  *
-  * We also use this to initialize the fields during deparse_context_for_plan.
-  */
- static void
- push_plan(deparse_namespace *dpns, Plan *subplan)
- {
-     /*
-      * We special-case Append to pretend that the first child plan is the
-      * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
-      * according to one of the children, and the first one is the most natural
-      * choice.    Likewise special-case ModifyTable to pretend that the first
-      * child plan is the OUTER referent; this is to support RETURNING lists
-      * containing references to non-target relations.
-      */
-     if (IsA(subplan, Append))
-         dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
-     else if (IsA(subplan, ModifyTable))
-         dpns->outer_plan = (Plan *) linitial(((ModifyTable *) subplan)->plans);
-     else
-         dpns->outer_plan = outerPlan(subplan);
-
-     /*
-      * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
-      * use OUTER because that could someday conflict with the normal meaning.)
-      * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
-      */
-     if (IsA(subplan, SubqueryScan))
-         dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
-     else if (IsA(subplan, CteScan))
-     {
-         int            ctePlanId = ((CteScan *) subplan)->ctePlanId;
-
-         if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
-             dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
-         else
-             dpns->inner_plan = NULL;
-     }
-     else
-         dpns->inner_plan = innerPlan(subplan);
- }
-
-
- /*
   * Display a Var appropriately.
   *
   * In some cases (currently only when recursing into an unnamed join)
--- 3625,3630 ----
***************
*** 3576,3592 ****
      else if (var->varno == OUTER && dpns->outer_plan)
      {
          TargetEntry *tle;
!         Plan       *save_outer;
!         Plan       *save_inner;

          tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         save_outer = dpns->outer_plan;
!         save_inner = dpns->inner_plan;
!         push_plan(dpns, dpns->outer_plan);

          /*
           * Force parentheses because our caller probably assumed a Var is a
--- 3669,3682 ----
      else if (var->varno == OUTER && dpns->outer_plan)
      {
          TargetEntry *tle;
!         deparse_namespace save_dpns;

          tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         push_child_plan(dpns, dpns->outer_planstate, &save_dpns);

          /*
           * Force parentheses because our caller probably assumed a Var is a
***************
*** 3598,3621 ****
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         dpns->outer_plan = save_outer;
!         dpns->inner_plan = save_inner;
          return NULL;
      }
      else if (var->varno == INNER && dpns->inner_plan)
      {
          TargetEntry *tle;
!         Plan       *save_outer;
!         Plan       *save_inner;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         save_outer = dpns->outer_plan;
!         save_inner = dpns->inner_plan;
!         push_plan(dpns, dpns->inner_plan);

          /*
           * Force parentheses because our caller probably assumed a Var is a
--- 3688,3707 ----
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         pop_child_plan(dpns, &save_dpns);
          return NULL;
      }
      else if (var->varno == INNER && dpns->inner_plan)
      {
          TargetEntry *tle;
!         deparse_namespace save_dpns;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         push_child_plan(dpns, dpns->inner_planstate, &save_dpns);

          /*
           * Force parentheses because our caller probably assumed a Var is a
***************
*** 3627,3634 ****
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         dpns->outer_plan = save_outer;
!         dpns->inner_plan = save_inner;
          return NULL;
      }
      else
--- 3713,3719 ----
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         pop_child_plan(dpns, &save_dpns);
          return NULL;
      }
      else
***************
*** 3653,3669 ****
          dpns->inner_plan)
      {
          TargetEntry *tle;
!         Plan       *save_outer;
!         Plan       *save_inner;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         save_outer = dpns->outer_plan;
!         save_inner = dpns->inner_plan;
!         push_plan(dpns, dpns->inner_plan);

          /*
           * Force parentheses because our caller probably assumed a Var is a
--- 3738,3751 ----
          dpns->inner_plan)
      {
          TargetEntry *tle;
!         deparse_namespace save_dpns;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
          if (!tle)
              elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         push_child_plan(dpns, dpns->inner_planstate, &save_dpns);

          /*
           * Force parentheses because our caller probably assumed a Var is a
***************
*** 3675,3682 ****
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         dpns->outer_plan = save_outer;
!         dpns->inner_plan = save_inner;
          return NULL;
      }

--- 3757,3763 ----
          if (!IsA(tle->expr, Var))
              appendStringInfoChar(buf, ')');

!         pop_child_plan(dpns, &save_dpns);
          return NULL;
      }

***************
*** 3827,3834 ****
      else if (var->varno == OUTER && dpns->outer_plan)
      {
          TargetEntry *tle;
!         Plan       *save_outer;
!         Plan       *save_inner;
          const char *result;

          tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
--- 3908,3914 ----
      else if (var->varno == OUTER && dpns->outer_plan)
      {
          TargetEntry *tle;
!         deparse_namespace save_dpns;
          const char *result;

          tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
***************
*** 3836,3857 ****
              elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         save_outer = dpns->outer_plan;
!         save_inner = dpns->inner_plan;
!         push_plan(dpns, dpns->outer_plan);

          result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                          levelsup, context);

!         dpns->outer_plan = save_outer;
!         dpns->inner_plan = save_inner;
          return result;
      }
      else if (var->varno == INNER && dpns->inner_plan)
      {
          TargetEntry *tle;
!         Plan       *save_outer;
!         Plan       *save_inner;
          const char *result;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
--- 3916,3933 ----
              elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         push_child_plan(dpns, dpns->outer_planstate, &save_dpns);

          result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                          levelsup, context);

!         pop_child_plan(dpns, &save_dpns);
          return result;
      }
      else if (var->varno == INNER && dpns->inner_plan)
      {
          TargetEntry *tle;
!         deparse_namespace save_dpns;
          const char *result;

          tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
***************
*** 3859,3873 ****
              elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         save_outer = dpns->outer_plan;
!         save_inner = dpns->inner_plan;
!         push_plan(dpns, dpns->inner_plan);

          result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                          levelsup, context);

!         dpns->outer_plan = save_outer;
!         dpns->inner_plan = save_inner;
          return result;
      }
      else
--- 3935,3946 ----
              elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);

          Assert(netlevelsup == 0);
!         push_child_plan(dpns, dpns->inner_planstate, &save_dpns);

          result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                          levelsup, context);

!         pop_child_plan(dpns, &save_dpns);
          return result;
      }
      else
***************
*** 3926,3935 ****
                          deparse_namespace mydpns;
                          const char *result;

                          mydpns.rtable = rte->subquery->rtable;
                          mydpns.ctes = rte->subquery->cteList;
-                         mydpns.subplans = NIL;
-                         mydpns.outer_plan = mydpns.inner_plan = NULL;

                          context->namespaces = lcons(&mydpns,
                                                      context->namespaces);
--- 3999,4007 ----
                          deparse_namespace mydpns;
                          const char *result;

+                         memset(&mydpns, 0, sizeof(mydpns));
                          mydpns.rtable = rte->subquery->rtable;
                          mydpns.ctes = rte->subquery->cteList;

                          context->namespaces = lcons(&mydpns,
                                                      context->namespaces);
***************
*** 3954,3961 ****
                       * look into the child plan's tlist instead.
                       */
                      TargetEntry *tle;
!                     Plan       *save_outer;
!                     Plan       *save_inner;
                      const char *result;

                      if (!dpns->inner_plan)
--- 4026,4032 ----
                       * look into the child plan's tlist instead.
                       */
                      TargetEntry *tle;
!                     deparse_namespace save_dpns;
                      const char *result;

                      if (!dpns->inner_plan)
***************
*** 3967,3981 ****
                          elog(ERROR, "bogus varattno for subquery var: %d",
                               attnum);
                      Assert(netlevelsup == 0);
!                     save_outer = dpns->outer_plan;
!                     save_inner = dpns->inner_plan;
!                     push_plan(dpns, dpns->inner_plan);

                      result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                      levelsup, context);

!                     dpns->outer_plan = save_outer;
!                     dpns->inner_plan = save_inner;
                      return result;
                  }
              }
--- 4038,4049 ----
                          elog(ERROR, "bogus varattno for subquery var: %d",
                               attnum);
                      Assert(netlevelsup == 0);
!                     push_child_plan(dpns, dpns->inner_planstate, &save_dpns);

                      result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                      levelsup, context);

!                     pop_child_plan(dpns, &save_dpns);
                      return result;
                  }
              }
***************
*** 4049,4058 ****
                          deparse_namespace mydpns;
                          const char *result;

                          mydpns.rtable = ctequery->rtable;
                          mydpns.ctes = ctequery->cteList;
-                         mydpns.subplans = NIL;
-                         mydpns.outer_plan = mydpns.inner_plan = NULL;

                          new_nslist = list_copy_tail(context->namespaces,
                                                      ctelevelsup);
--- 4117,4125 ----
                          deparse_namespace mydpns;
                          const char *result;

+                         memset(&mydpns, 0, sizeof(mydpns));
                          mydpns.rtable = ctequery->rtable;
                          mydpns.ctes = ctequery->cteList;

                          new_nslist = list_copy_tail(context->namespaces,
                                                      ctelevelsup);
***************
*** 4076,4083 ****
                       * can look into the subplan's tlist instead.
                       */
                      TargetEntry *tle;
!                     Plan       *save_outer;
!                     Plan       *save_inner;
                      const char *result;

                      if (!dpns->inner_plan)
--- 4143,4149 ----
                       * can look into the subplan's tlist instead.
                       */
                      TargetEntry *tle;
!                     deparse_namespace save_dpns;
                      const char *result;

                      if (!dpns->inner_plan)
***************
*** 4089,4103 ****
                          elog(ERROR, "bogus varattno for subquery var: %d",
                               attnum);
                      Assert(netlevelsup == 0);
!                     save_outer = dpns->outer_plan;
!                     save_inner = dpns->inner_plan;
!                     push_plan(dpns, dpns->inner_plan);

                      result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                      levelsup, context);

!                     dpns->outer_plan = save_outer;
!                     dpns->inner_plan = save_inner;
                      return result;
                  }
              }
--- 4155,4166 ----
                          elog(ERROR, "bogus varattno for subquery var: %d",
                               attnum);
                      Assert(netlevelsup == 0);
!                     push_child_plan(dpns, dpns->inner_planstate, &save_dpns);

                      result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                      levelsup, context);

!                     pop_child_plan(dpns, &save_dpns);
                      return result;
                  }
              }
***************
*** 4160,4165 ****
--- 4223,4387 ----
      return result;
  }

+ /*
+  * Display a Param appropriately.
+  */
+ static void
+ get_parameter(Param *param, deparse_context *context)
+ {
+     /*
+      * If it's a PARAM_EXEC parameter, try to locate the expression from
+      * which the parameter was computed.  This will necessarily be in some
+      * ancestor of the current expression's PlanState.  Note that failing
+      * to find a referent isn't an error, since the Param might well be a
+      * subplan output rather than an input.
+      */
+     if (param->paramkind == PARAM_EXEC)
+     {
+         deparse_namespace *dpns;
+         PlanState  *child_ps;
+         bool        in_same_plan_level;
+         ListCell   *lc;
+
+         dpns = (deparse_namespace *) linitial(context->namespaces);
+         child_ps = dpns->planstate;
+         in_same_plan_level = true;
+
+         foreach(lc, dpns->ancestors)
+         {
+             PlanState  *ps = (PlanState *) lfirst(lc);
+             ListCell   *lc2;
+
+             /*
+              * NestLoops transmit params to their inner child only; also,
+              * once we've crawled up out of a subplan, this couldn't
+              * possibly be the right match.
+              */
+             if (IsA(ps, NestLoopState) &&
+                 child_ps == innerPlanState(ps) &&
+                 in_same_plan_level)
+             {
+                 NestLoop   *nl = (NestLoop *) ps->plan;
+
+                 foreach(lc2, nl->nestParams)
+                 {
+                     NestLoopParam  *nlp = (NestLoopParam *) lfirst(lc2);
+
+                     if (nlp->paramno == param->paramid)
+                     {
+                         /* Found a match, so print it */
+                         print_parameter_expr((Node *) nlp->paramval, lc,
+                                              dpns, context);
+                         return;
+                     }
+                 }
+             }
+
+             /*
+              * Check to see if we're crawling up from an initPlan.
+              */
+             foreach(lc2, ps->initPlan)
+             {
+                 SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+                 SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+                 ListCell   *lc3;
+                 ListCell   *lc4;
+
+                 if (child_ps != sstate->planstate)
+                     continue;
+
+                 /* Matched initPlan, so check its arguments */
+                 forboth(lc3, subplan->parParam, lc4, subplan->args)
+                 {
+                     int        paramid = lfirst_int(lc3);
+                     Node   *arg = (Node *) lfirst(lc4);
+
+                     if (paramid == param->paramid)
+                     {
+                         /* Found a match, so print it */
+                         print_parameter_expr(arg, lc, dpns, context);
+                         return;
+                     }
+                 }
+
+                 /* Keep looking, but we are emerging from an initPlan. */
+                 in_same_plan_level = false;
+                 break;
+             }
+
+             /*
+              * Likewise for subPlans.
+              */
+             foreach(lc2, ps->subPlan)
+             {
+                 SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+                 SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+                 ListCell   *lc3;
+                 ListCell   *lc4;
+
+                 if (child_ps != sstate->planstate)
+                     continue;
+
+                 /* Matched subPlan, so check its arguments */
+                 forboth(lc3, subplan->parParam, lc4, subplan->args)
+                 {
+                     int        paramid = lfirst_int(lc3);
+                     Node   *arg = (Node *) lfirst(lc4);
+
+                     if (paramid == param->paramid)
+                     {
+                         /* Found a match, so print it */
+                         print_parameter_expr(arg, lc, dpns, context);
+                         return;
+                     }
+                 }
+
+                 /* Keep looking, but we are emerging from a subPlan. */
+                 in_same_plan_level = false;
+                 break;
+             }
+
+             /* No luck, crawl up to next ancestor */
+             child_ps = ps;
+         }
+     }
+
+     /*
+      * Not PARAM_EXEC, or couldn't find referent: just print $N.
+      */
+     appendStringInfo(context->buf, "$%d", param->paramid);
+ }
+
+ /* Print a parameter reference expression found by get_parameter */
+ static void
+ print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+                      deparse_namespace *dpns, deparse_context *context)
+ {
+     deparse_namespace save_dpns;
+     bool        save_varprefix;
+
+     /* Switch attention to the ancestor plan node */
+     push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+
+     /*
+      * Force prefixing of Vars, since they won't belong to the relation being
+      * scanned in the original plan node.
+      */
+     save_varprefix = context->varprefix;
+     context->varprefix = true;
+
+     /*
+      * We don't need to add parentheses because a Param's expansion is
+      * (currently) always a Var or Aggref.
+      */
+     Assert(IsA(expr, Var) || IsA(expr, Aggref));
+
+     get_rule_expr(expr, context, false);
+
+     context->varprefix = save_varprefix;
+
+     pop_ancestor_plan(dpns, &save_dpns);
+ }

  /*
   * get_simple_binary_op_name
***************
*** 4496,4502 ****
              break;

          case T_Param:
!             appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
              break;

          case T_Aggref:
--- 4718,4724 ----
              break;

          case T_Param:
!             get_parameter((Param *) node, context);
              break;

          case T_Aggref:
Index: src/include/utils/builtins.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.350
diff -c -r1.350 builtins.h
*** src/include/utils/builtins.h    6 Jul 2010 19:19:00 -0000    1.350
--- src/include/utils/builtins.h    13 Jul 2010 18:13:25 -0000
***************
*** 609,616 ****
  extern char *deparse_expression(Node *expr, List *dpcontext,
                     bool forceprefix, bool showimplicit);
  extern List *deparse_context_for(const char *aliasname, Oid relid);
! extern List *deparse_context_for_plan(Node *plan, Node *outer_plan,
!                          List *rtable, List *subplans);
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *qualifier,
                             const char *ident);
--- 609,616 ----
  extern char *deparse_expression(Node *expr, List *dpcontext,
                     bool forceprefix, bool showimplicit);
  extern List *deparse_context_for(const char *aliasname, Oid relid);
! extern List *deparse_context_for_planstate(Node *planstate, List *ancestors,
!                               List *rtable);
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *qualifier,
                             const char *ident);

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

Предыдущее
От: Thom Brown
Дата:
Сообщение: Re: ERROR: argument to pg_get_expr() must come from system catalogs
Следующее
От: Peter Eisentraut
Дата:
Сообщение: Per-column collation, proof of concept