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