OSDN Git Service

bf8b6fb27baf0eb9f2beea4955216e877859df0f
[pg-rex/syncrep.git] / src / backend / commands / explain.c
1 /*
2  * explain.c
3  *        Explain the query execution plan
4  *
5  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994-5, Regents of the University of California
7  *
8  * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.86 2002/09/02 01:05:04 tgl Exp $
9  *
10  */
11
12 #include "postgres.h"
13
14 #include "access/genam.h"
15 #include "access/heapam.h"
16 #include "catalog/pg_type.h"
17 #include "commands/explain.h"
18 #include "executor/executor.h"
19 #include "executor/instrument.h"
20 #include "lib/stringinfo.h"
21 #include "nodes/print.h"
22 #include "optimizer/clauses.h"
23 #include "optimizer/planner.h"
24 #include "optimizer/var.h"
25 #include "parser/parsetree.h"
26 #include "rewrite/rewriteHandler.h"
27 #include "tcop/pquery.h"
28 #include "utils/builtins.h"
29 #include "utils/guc.h"
30 #include "utils/lsyscache.h"
31
32
33 typedef struct ExplainState
34 {
35         /* options */
36         bool            printCost;              /* print cost */
37         bool            printNodes;             /* do nodeToString() instead */
38         /* other states */
39         List       *rtable;                     /* range table */
40 } ExplainState;
41
42 static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
43 static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
44                                                         TupOutputState *tstate);
45 static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
46                                                         int indent, ExplainState *es);
47 static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
48                                                    int scanrelid, Plan *outer_plan,
49                                                    StringInfo str, int indent, ExplainState *es);
50 static void show_upper_qual(List *qual, const char *qlabel,
51                                                         const char *outer_name, int outer_varno, Plan *outer_plan,
52                                                         const char *inner_name, int inner_varno, Plan *inner_plan,
53                                                         StringInfo str, int indent, ExplainState *es);
54 static void show_sort_keys(List *tlist, int nkeys, const char *qlabel,
55                                                    StringInfo str, int indent, ExplainState *es);
56 static Node *make_ors_ands_explicit(List *orclauses);
57
58 /*
59  * ExplainQuery -
60  *        execute an EXPLAIN command
61  */
62 void
63 ExplainQuery(ExplainStmt *stmt, CommandDest dest)
64 {
65         Query      *query = stmt->query;
66         TupOutputState *tstate;
67         TupleDesc       tupdesc;
68         List       *rewritten;
69         List       *l;
70
71         /* need a tuple descriptor representing a single TEXT column */
72         tupdesc = CreateTemplateTupleDesc(1, false);
73         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
74                                            TEXTOID, -1, 0, false);
75
76         /* prepare for projection of tuples */
77         tstate = begin_tup_output_tupdesc(dest, tupdesc);
78
79         if (query->commandType == CMD_UTILITY)
80         {
81                 /* rewriter will not cope with utility statements */
82                 do_text_output_oneline(tstate, "Utility statements have no plan structure");
83         }
84         else
85         {
86                 /* Rewrite through rule system */
87                 rewritten = QueryRewrite(query);
88
89                 if (rewritten == NIL)
90                 {
91                         /* In the case of an INSTEAD NOTHING, tell at least that */
92                         do_text_output_oneline(tstate, "Query rewrites to nothing");
93                 }
94                 else
95                 {
96                         /* Explain every plan */
97                         foreach(l, rewritten)
98                         {
99                                 ExplainOneQuery(lfirst(l), stmt, tstate);
100                                 /* put a blank line between plans */
101                                 if (lnext(l) != NIL)
102                                         do_text_output_oneline(tstate, "");
103                         }
104                 }
105         }
106
107         end_tup_output(tstate);
108 }
109
110 /*
111  * ExplainOneQuery -
112  *        print out the execution plan for one query
113  */
114 static void
115 ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
116 {
117         Plan       *plan;
118         ExplainState *es;
119         double          totaltime = 0;
120
121         /* planner will not cope with utility statements */
122         if (query->commandType == CMD_UTILITY)
123         {
124                 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
125                         do_text_output_oneline(tstate, "NOTIFY");
126                 else
127                         do_text_output_oneline(tstate, "UTILITY");
128                 return;
129         }
130
131         /* plan the query */
132         plan = planner(query);
133
134         /* pg_plan could have failed */
135         if (plan == NULL)
136                 return;
137
138         /* Execute the plan for statistics if asked for */
139         if (stmt->analyze)
140         {
141                 struct timeval starttime;
142                 struct timeval endtime;
143
144                 /*
145                  * Set up the instrumentation for the top node. This will cascade
146                  * during plan initialisation
147                  */
148                 plan->instrument = InstrAlloc();
149
150                 gettimeofday(&starttime, NULL);
151                 ProcessQuery(query, plan, None, NULL);
152                 CommandCounterIncrement();
153                 gettimeofday(&endtime, NULL);
154
155                 endtime.tv_sec -= starttime.tv_sec;
156                 endtime.tv_usec -= starttime.tv_usec;
157                 while (endtime.tv_usec < 0)
158                 {
159                         endtime.tv_usec += 1000000;
160                         endtime.tv_sec--;
161                 }
162                 totaltime = (double) endtime.tv_sec +
163                         (double) endtime.tv_usec / 1000000.0;
164         }
165
166         es = (ExplainState *) palloc(sizeof(ExplainState));
167         MemSet(es, 0, sizeof(ExplainState));
168
169         es->printCost = true;           /* default */
170
171         if (stmt->verbose)
172                 es->printNodes = true;
173
174         es->rtable = query->rtable;
175
176         if (es->printNodes)
177         {
178                 char       *s;
179                 char       *f;
180
181                 s = nodeToString(plan);
182                 if (s)
183                 {
184                         if (Explain_pretty_print)
185                                 f = pretty_format_node_dump(s);
186                         else
187                                 f = format_node_dump(s);
188                         pfree(s);
189                         do_text_output_multiline(tstate, f);
190                         pfree(f);
191                         if (es->printCost)
192                                 do_text_output_oneline(tstate, "");     /* separator line */
193                 }
194         }
195
196         if (es->printCost)
197         {
198                 StringInfo      str;
199
200                 str = Explain_PlanToString(plan, es);
201                 if (stmt->analyze)
202                         appendStringInfo(str, "Total runtime: %.2f msec\n",
203                                                          1000.0 * totaltime);
204                 do_text_output_multiline(tstate, str->data);
205                 pfree(str->data);
206                 pfree(str);
207         }
208
209         pfree(es);
210 }
211
212
213 /*
214  * explain_outNode -
215  *        converts a Plan node into ascii string and appends it to 'str'
216  *
217  * outer_plan, if not null, references another plan node that is the outer
218  * side of a join with the current node.  This is only interesting for
219  * deciphering runtime keys of an inner indexscan.
220  */
221 static void
222 explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
223                                 int indent, ExplainState *es)
224 {
225         List       *l;
226         char       *pname;
227         int                     i;
228
229         if (plan == NULL)
230         {
231                 appendStringInfo(str, "\n");
232                 return;
233         }
234
235         switch (nodeTag(plan))
236         {
237                 case T_Result:
238                         pname = "Result";
239                         break;
240                 case T_Append:
241                         pname = "Append";
242                         break;
243                 case T_NestLoop:
244                         pname = "Nested Loop";
245                         break;
246                 case T_MergeJoin:
247                         pname = "Merge Join";
248                         break;
249                 case T_HashJoin:
250                         pname = "Hash Join";
251                         break;
252                 case T_SeqScan:
253                         pname = "Seq Scan";
254                         break;
255                 case T_IndexScan:
256                         pname = "Index Scan";
257                         break;
258                 case T_TidScan:
259                         pname = "Tid Scan";
260                         break;
261                 case T_SubqueryScan:
262                         pname = "Subquery Scan";
263                         break;
264                 case T_FunctionScan:
265                         pname = "Function Scan";
266                         break;
267                 case T_Material:
268                         pname = "Materialize";
269                         break;
270                 case T_Sort:
271                         pname = "Sort";
272                         break;
273                 case T_Group:
274                         pname = "Group";
275                         break;
276                 case T_Agg:
277                         pname = "Aggregate";
278                         break;
279                 case T_Unique:
280                         pname = "Unique";
281                         break;
282                 case T_SetOp:
283                         switch (((SetOp *) plan)->cmd)
284                         {
285                                 case SETOPCMD_INTERSECT:
286                                         pname = "SetOp Intersect";
287                                         break;
288                                 case SETOPCMD_INTERSECT_ALL:
289                                         pname = "SetOp Intersect All";
290                                         break;
291                                 case SETOPCMD_EXCEPT:
292                                         pname = "SetOp Except";
293                                         break;
294                                 case SETOPCMD_EXCEPT_ALL:
295                                         pname = "SetOp Except All";
296                                         break;
297                                 default:
298                                         pname = "SetOp ???";
299                                         break;
300                         }
301                         break;
302                 case T_Limit:
303                         pname = "Limit";
304                         break;
305                 case T_Hash:
306                         pname = "Hash";
307                         break;
308                 default:
309                         pname = "???";
310                         break;
311         }
312
313         appendStringInfo(str, pname);
314         switch (nodeTag(plan))
315         {
316                 case T_IndexScan:
317                         if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
318                                 appendStringInfo(str, " Backward");
319                         appendStringInfo(str, " using ");
320                         i = 0;
321                         foreach(l, ((IndexScan *) plan)->indxid)
322                         {
323                                 Relation        relation;
324
325                                 relation = index_open(lfirsti(l));
326                                 appendStringInfo(str, "%s%s",
327                                                                  (++i > 1) ? ", " : "",
328                                                                  quote_identifier(RelationGetRelationName(relation)));
329                                 index_close(relation);
330                         }
331                         /* FALL THRU */
332                 case T_SeqScan:
333                 case T_TidScan:
334                         if (((Scan *) plan)->scanrelid > 0)
335                         {
336                                 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
337                                                                                           es->rtable);
338                                 char   *relname;
339
340                                 /* Assume it's on a real relation */
341                                 Assert(rte->rtekind == RTE_RELATION);
342
343                                 /* We only show the rel name, not schema name */
344                                 relname = get_rel_name(rte->relid);
345
346                                 appendStringInfo(str, " on %s",
347                                                                  quote_identifier(relname));
348                                 if (strcmp(rte->eref->aliasname, relname) != 0)
349                                         appendStringInfo(str, " %s",
350                                                                          quote_identifier(rte->eref->aliasname));
351                         }
352                         break;
353                 case T_SubqueryScan:
354                         if (((Scan *) plan)->scanrelid > 0)
355                         {
356                                 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
357                                                                                           es->rtable);
358
359                                 appendStringInfo(str, " %s",
360                                                                  quote_identifier(rte->eref->aliasname));
361                         }
362                         break;
363                 case T_FunctionScan:
364                         if (((Scan *) plan)->scanrelid > 0)
365                         {
366                                 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
367                                                                                           es->rtable);
368                                 Expr   *expr;
369                                 Func   *funcnode;
370                                 Oid             funcid;
371                                 char   *proname;
372
373                                 /* Assert it's on a RangeFunction */
374                                 Assert(rte->rtekind == RTE_FUNCTION);
375
376                                 expr = (Expr *) rte->funcexpr;
377                                 funcnode = (Func *) expr->oper;
378                                 funcid = funcnode->funcid;
379
380                                 /* We only show the func name, not schema name */
381                                 proname = get_func_name(funcid);
382
383                                 appendStringInfo(str, " on %s",
384                                                                  quote_identifier(proname));
385                                 if (strcmp(rte->eref->aliasname, proname) != 0)
386                                         appendStringInfo(str, " %s",
387                                                                          quote_identifier(rte->eref->aliasname));
388                         }
389                         break;
390                 default:
391                         break;
392         }
393         if (es->printCost)
394         {
395                 appendStringInfo(str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
396                                                  plan->startup_cost, plan->total_cost,
397                                                  plan->plan_rows, plan->plan_width);
398
399                 if (plan->instrument && plan->instrument->nloops > 0)
400                 {
401                         double          nloops = plan->instrument->nloops;
402
403                         appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
404                                                          1000.0 * plan->instrument->startup / nloops,
405                                                          1000.0 * plan->instrument->total / nloops,
406                                                          plan->instrument->ntuples / nloops,
407                                                          plan->instrument->nloops);
408                 }
409         }
410         appendStringInfo(str, "\n");
411
412         /* quals, sort keys, etc */
413         switch (nodeTag(plan))
414         {
415                 case T_IndexScan:
416                         show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
417                                                    "Index Cond",
418                                                    ((Scan *) plan)->scanrelid,
419                                                    outer_plan,
420                                                    str, indent, es);
421                         show_scan_qual(plan->qual, false,
422                                                    "Filter",
423                                                    ((Scan *) plan)->scanrelid,
424                                                    outer_plan,
425                                                    str, indent, es);
426                         break;
427                 case T_SeqScan:
428                 case T_TidScan:
429                 case T_FunctionScan:
430                         show_scan_qual(plan->qual, false,
431                                                    "Filter",
432                                                    ((Scan *) plan)->scanrelid,
433                                                    outer_plan,
434                                                    str, indent, es);
435                         break;
436                 case T_NestLoop:
437                         show_upper_qual(((NestLoop *) plan)->join.joinqual,
438                                                         "Join Filter",
439                                                         "outer", OUTER, outerPlan(plan),
440                                                         "inner", INNER, innerPlan(plan),
441                                                         str, indent, es);
442                         show_upper_qual(plan->qual,
443                                                         "Filter",
444                                                         "outer", OUTER, outerPlan(plan),
445                                                         "inner", INNER, innerPlan(plan),
446                                                         str, indent, es);
447                         break;
448                 case T_MergeJoin:
449                         show_upper_qual(((MergeJoin *) plan)->mergeclauses,
450                                                         "Merge Cond",
451                                                         "outer", OUTER, outerPlan(plan),
452                                                         "inner", INNER, innerPlan(plan),
453                                                         str, indent, es);
454                         show_upper_qual(((MergeJoin *) plan)->join.joinqual,
455                                                         "Join Filter",
456                                                         "outer", OUTER, outerPlan(plan),
457                                                         "inner", INNER, innerPlan(plan),
458                                                         str, indent, es);
459                         show_upper_qual(plan->qual,
460                                                         "Filter",
461                                                         "outer", OUTER, outerPlan(plan),
462                                                         "inner", INNER, innerPlan(plan),
463                                                         str, indent, es);
464                         break;
465                 case T_HashJoin:
466                         show_upper_qual(((HashJoin *) plan)->hashclauses,
467                                                         "Hash Cond",
468                                                         "outer", OUTER, outerPlan(plan),
469                                                         "inner", INNER, innerPlan(plan),
470                                                         str, indent, es);
471                         show_upper_qual(((HashJoin *) plan)->join.joinqual,
472                                                         "Join Filter",
473                                                         "outer", OUTER, outerPlan(plan),
474                                                         "inner", INNER, innerPlan(plan),
475                                                         str, indent, es);
476                         show_upper_qual(plan->qual,
477                                                         "Filter",
478                                                         "outer", OUTER, outerPlan(plan),
479                                                         "inner", INNER, innerPlan(plan),
480                                                         str, indent, es);
481                         break;
482                 case T_SubqueryScan:
483                         show_upper_qual(plan->qual,
484                                                         "Filter",
485                                                         "subplan", 1, ((SubqueryScan *) plan)->subplan,
486                                                         "", 0, NULL,
487                                                         str, indent, es);
488                         break;
489                 case T_Agg:
490                 case T_Group:
491                         show_upper_qual(plan->qual,
492                                                         "Filter",
493                                                         "subplan", 0, outerPlan(plan),
494                                                         "", 0, NULL,
495                                                         str, indent, es);
496                         break;
497                 case T_Sort:
498                         show_sort_keys(plan->targetlist, ((Sort *) plan)->keycount,
499                                                    "Sort Key",
500                                                    str, indent, es);
501                         break;
502                 case T_Result:
503                         show_upper_qual((List *) ((Result *) plan)->resconstantqual,
504                                                         "One-Time Filter",
505                                                         "subplan", OUTER, outerPlan(plan),
506                                                         "", 0, NULL,
507                                                         str, indent, es);
508                         show_upper_qual(plan->qual,
509                                                         "Filter",
510                                                         "subplan", OUTER, outerPlan(plan),
511                                                         "", 0, NULL,
512                                                         str, indent, es);
513                         break;
514                 default:
515                         break;
516         }
517
518         /* initPlan-s */
519         if (plan->initPlan)
520         {
521                 List       *saved_rtable = es->rtable;
522                 List       *lst;
523
524                 for (i = 0; i < indent; i++)
525                         appendStringInfo(str, "  ");
526                 appendStringInfo(str, "  InitPlan\n");
527                 foreach(lst, plan->initPlan)
528                 {
529                         es->rtable = ((SubPlan *) lfirst(lst))->rtable;
530                         for (i = 0; i < indent; i++)
531                                 appendStringInfo(str, "  ");
532                         appendStringInfo(str, "    ->  ");
533                         explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
534                                                         indent + 4, es);
535                 }
536                 es->rtable = saved_rtable;
537         }
538
539         /* lefttree */
540         if (outerPlan(plan))
541         {
542                 for (i = 0; i < indent; i++)
543                         appendStringInfo(str, "  ");
544                 appendStringInfo(str, "  ->  ");
545                 explain_outNode(str, outerPlan(plan), NULL, indent + 3, es);
546         }
547
548         /* righttree */
549         if (innerPlan(plan))
550         {
551                 for (i = 0; i < indent; i++)
552                         appendStringInfo(str, "  ");
553                 appendStringInfo(str, "  ->  ");
554                 explain_outNode(str, innerPlan(plan), outerPlan(plan),
555                                                 indent + 3, es);
556         }
557
558         if (IsA(plan, Append))
559         {
560                 Append     *appendplan = (Append *) plan;
561                 List       *lst;
562
563                 foreach(lst, appendplan->appendplans)
564                 {
565                         Plan       *subnode = (Plan *) lfirst(lst);
566
567                         for (i = 0; i < indent; i++)
568                                 appendStringInfo(str, "  ");
569                         appendStringInfo(str, "  ->  ");
570
571                         explain_outNode(str, subnode, NULL, indent + 3, es);
572                 }
573         }
574
575         if (IsA(plan, SubqueryScan))
576         {
577                 SubqueryScan *subqueryscan = (SubqueryScan *) plan;
578                 Plan       *subnode = subqueryscan->subplan;
579                 RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
580                                                                           es->rtable);
581                 List       *saved_rtable = es->rtable;
582
583                 Assert(rte->rtekind == RTE_SUBQUERY);
584                 es->rtable = rte->subquery->rtable;
585
586                 for (i = 0; i < indent; i++)
587                         appendStringInfo(str, "  ");
588                 appendStringInfo(str, "  ->  ");
589
590                 explain_outNode(str, subnode, NULL, indent + 3, es);
591
592                 es->rtable = saved_rtable;
593         }
594
595         /* subPlan-s */
596         if (plan->subPlan)
597         {
598                 List       *saved_rtable = es->rtable;
599                 List       *lst;
600
601                 for (i = 0; i < indent; i++)
602                         appendStringInfo(str, "  ");
603                 appendStringInfo(str, "  SubPlan\n");
604                 foreach(lst, plan->subPlan)
605                 {
606                         es->rtable = ((SubPlan *) lfirst(lst))->rtable;
607                         for (i = 0; i < indent; i++)
608                                 appendStringInfo(str, "  ");
609                         appendStringInfo(str, "    ->  ");
610                         explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
611                                                         indent + 4, es);
612                 }
613                 es->rtable = saved_rtable;
614         }
615 }
616
617 static StringInfo
618 Explain_PlanToString(Plan *plan, ExplainState *es)
619 {
620         StringInfo      str = makeStringInfo();
621
622         if (plan != NULL)
623                 explain_outNode(str, plan, NULL, 0, es);
624         return str;
625 }
626
627 /*
628  * Show a qualifier expression for a scan plan node
629  */
630 static void
631 show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
632                            int scanrelid, Plan *outer_plan,
633                            StringInfo str, int indent, ExplainState *es)
634 {
635         RangeTblEntry *rte;
636         Node       *scancontext;
637         Node       *outercontext;
638         List       *context;
639         Node       *node;
640         char       *exprstr;
641         int                     i;
642
643         /* No work if empty qual */
644         if (qual == NIL)
645                 return;
646         if (is_or_qual)
647         {
648                 if (lfirst(qual) == NIL && lnext(qual) == NIL)
649                         return;
650         }
651
652         /* Fix qual --- indexqual requires different processing */
653         if (is_or_qual)
654                 node = make_ors_ands_explicit(qual);
655         else
656                 node = (Node *) make_ands_explicit(qual);
657
658         /* Generate deparse context */
659         Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
660         rte = rt_fetch(scanrelid, es->rtable);
661         scancontext = deparse_context_for_rte(rte);
662
663         /*
664          * If we have an outer plan that is referenced by the qual, add it to
665          * the deparse context.  If not, don't (so that we don't force prefixes
666          * unnecessarily).
667          */
668         if (outer_plan)
669         {
670                 if (intMember(OUTER, pull_varnos(node)))
671                         outercontext = deparse_context_for_subplan("outer",
672                                                                                                            outer_plan->targetlist,
673                                                                                                            es->rtable);
674                 else
675                         outercontext = NULL;
676         }
677         else
678                 outercontext = NULL;
679
680         context = deparse_context_for_plan(scanrelid, scancontext,
681                                                                            OUTER, outercontext,
682                                                                            NIL);
683
684         /* Deparse the expression */
685         exprstr = deparse_expression(node, context, (outercontext != NULL));
686
687         /* And add to str */
688         for (i = 0; i < indent; i++)
689                 appendStringInfo(str, "  ");
690         appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
691 }
692
693 /*
694  * Show a qualifier expression for an upper-level plan node
695  */
696 static void
697 show_upper_qual(List *qual, const char *qlabel,
698                                 const char *outer_name, int outer_varno, Plan *outer_plan,
699                                 const char *inner_name, int inner_varno, Plan *inner_plan,
700                                 StringInfo str, int indent, ExplainState *es)
701 {
702         List       *context;
703         Node       *outercontext;
704         Node       *innercontext;
705         Node       *node;
706         char       *exprstr;
707         int                     i;
708
709         /* No work if empty qual */
710         if (qual == NIL)
711                 return;
712
713         /* Generate deparse context */
714         if (outer_plan)
715                 outercontext = deparse_context_for_subplan(outer_name,
716                                                                                                    outer_plan->targetlist,
717                                                                                                    es->rtable);
718         else
719                 outercontext = NULL;
720         if (inner_plan)
721                 innercontext = deparse_context_for_subplan(inner_name,
722                                                                                                    inner_plan->targetlist,
723                                                                                                    es->rtable);
724         else
725                 innercontext = NULL;
726         context = deparse_context_for_plan(outer_varno, outercontext,
727                                                                            inner_varno, innercontext,
728                                                                            NIL);
729
730         /* Deparse the expression */
731         node = (Node *) make_ands_explicit(qual);
732         exprstr = deparse_expression(node, context, (inner_plan != NULL));
733
734         /* And add to str */
735         for (i = 0; i < indent; i++)
736                 appendStringInfo(str, "  ");
737         appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
738 }
739
740 /*
741  * Show the sort keys for a Sort node.
742  */
743 static void
744 show_sort_keys(List *tlist, int nkeys, const char *qlabel,
745                            StringInfo str, int indent, ExplainState *es)
746 {
747         List       *context;
748         bool            useprefix;
749         int                     keyno;
750         List       *tl;
751         char       *exprstr;
752         int                     i;
753
754         if (nkeys <= 0)
755                 return;
756
757         for (i = 0; i < indent; i++)
758                 appendStringInfo(str, "  ");
759         appendStringInfo(str, "  %s: ", qlabel);
760
761         /*
762          * In this routine we expect that the plan node's tlist has not been
763          * processed by set_plan_references().  Normally, any Vars will contain
764          * valid varnos referencing the actual rtable.  But we might instead be
765          * looking at a dummy tlist generated by prepunion.c; if there are
766          * Vars with zero varno, use the tlist itself to determine their names.
767          */
768         if (intMember(0, pull_varnos((Node *) tlist)))
769         {
770                 Node       *outercontext;
771
772                 outercontext = deparse_context_for_subplan("sort",
773                                                                                                    tlist,
774                                                                                                    es->rtable);
775                 context = deparse_context_for_plan(0, outercontext,
776                                                                                    0, NULL,
777                                                                                    NIL);
778                 useprefix = false;
779         }
780         else
781         {
782                 context = deparse_context_for_plan(0, NULL,
783                                                                                    0, NULL,
784                                                                                    es->rtable);
785                 useprefix = length(es->rtable) > 1;
786         }
787
788         for (keyno = 1; keyno <= nkeys; keyno++)
789         {
790                 /* find key expression in tlist */
791                 foreach(tl, tlist)
792                 {
793                         TargetEntry *target = (TargetEntry *) lfirst(tl);
794
795                         if (target->resdom->reskey == keyno)
796                         {
797                                 /* Deparse the expression */
798                                 exprstr = deparse_expression(target->expr, context, useprefix);
799                                 /* And add to str */
800                                 if (keyno > 1)
801                                         appendStringInfo(str, ", ");
802                                 appendStringInfo(str, "%s", exprstr);
803                                 break;
804                         }
805                 }
806                 if (tl == NIL)
807                         elog(ERROR, "show_sort_keys: no tlist entry for key %d", keyno);
808         }
809
810         appendStringInfo(str, "\n");
811 }
812
813 /*
814  * Indexscan qual lists have an implicit OR-of-ANDs structure.  Make it
815  * explicit so deparsing works properly.
816  */
817 static Node *
818 make_ors_ands_explicit(List *orclauses)
819 {
820         if (orclauses == NIL)
821                 return NULL;                    /* probably can't happen */
822         else if (lnext(orclauses) == NIL)
823                 return (Node *) make_ands_explicit(lfirst(orclauses));
824         else
825         {
826                 List   *args = NIL;
827                 List   *orptr;
828
829                 foreach(orptr, orclauses)
830                 {
831                         args = lappend(args, make_ands_explicit(lfirst(orptr)));
832                 }
833
834                 return (Node *) make_orclause(args);
835         }
836 }