~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/commands/explain.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * explain.c
 
4
 *        Explain query execution plans
 
5
 *
 
6
 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994-5, Regents of the University of California
 
8
 *
 
9
 * IDENTIFICATION
 
10
 *        src/backend/commands/explain.c
 
11
 *
 
12
 *-------------------------------------------------------------------------
 
13
 */
 
14
#include "postgres.h"
 
15
 
 
16
#include "access/xact.h"
 
17
#include "catalog/pg_constraint.h"
 
18
#include "catalog/pg_type.h"
 
19
#include "commands/defrem.h"
 
20
#include "commands/explain.h"
 
21
#include "commands/prepare.h"
 
22
#include "commands/trigger.h"
 
23
#include "executor/hashjoin.h"
 
24
#include "executor/instrument.h"
 
25
#include "foreign/fdwapi.h"
 
26
#include "optimizer/clauses.h"
 
27
#include "optimizer/planner.h"
 
28
#include "optimizer/var.h"
 
29
#include "parser/parsetree.h"
 
30
#include "rewrite/rewriteHandler.h"
 
31
#include "tcop/tcopprot.h"
 
32
#include "utils/builtins.h"
 
33
#include "utils/guc.h"
 
34
#include "utils/lsyscache.h"
 
35
#include "utils/tuplesort.h"
 
36
#include "utils/snapmgr.h"
 
37
#include "utils/xml.h"
 
38
 
 
39
 
 
40
/* Hook for plugins to get control in ExplainOneQuery() */
 
41
ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
 
42
 
 
43
/* Hook for plugins to get control in explain_get_index_name() */
 
44
explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
 
45
 
 
46
 
 
47
/* OR-able flags for ExplainXMLTag() */
 
48
#define X_OPENING 0
 
49
#define X_CLOSING 1
 
50
#define X_CLOSE_IMMEDIATE 2
 
51
#define X_NOWHITESPACE 4
 
52
 
 
53
static void ExplainOneQuery(Query *query, ExplainState *es,
 
54
                                const char *queryString, ParamListInfo params);
 
55
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
 
56
                                ExplainState *es);
 
57
static double elapsed_time(instr_time *starttime);
 
58
static void ExplainNode(PlanState *planstate, List *ancestors,
 
59
                        const char *relationship, const char *plan_name,
 
60
                        ExplainState *es);
 
61
static void show_plan_tlist(PlanState *planstate, List *ancestors,
 
62
                                ExplainState *es);
 
63
static void show_expression(Node *node, const char *qlabel,
 
64
                                PlanState *planstate, List *ancestors,
 
65
                                bool useprefix, ExplainState *es);
 
66
static void show_qual(List *qual, const char *qlabel,
 
67
                  PlanState *planstate, List *ancestors,
 
68
                  bool useprefix, ExplainState *es);
 
69
static void show_scan_qual(List *qual, const char *qlabel,
 
70
                           PlanState *planstate, List *ancestors,
 
71
                           ExplainState *es);
 
72
static void show_upper_qual(List *qual, const char *qlabel,
 
73
                                PlanState *planstate, List *ancestors,
 
74
                                ExplainState *es);
 
75
static void show_sort_keys(SortState *sortstate, List *ancestors,
 
76
                           ExplainState *es);
 
77
static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
 
78
                                           ExplainState *es);
 
79
static void show_sort_keys_common(PlanState *planstate,
 
80
                                          int nkeys, AttrNumber *keycols,
 
81
                                          List *ancestors, ExplainState *es);
 
82
static void show_sort_info(SortState *sortstate, ExplainState *es);
 
83
static void show_hash_info(HashState *hashstate, ExplainState *es);
 
84
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
 
85
static const char *explain_get_index_name(Oid indexId);
 
86
static void ExplainScanTarget(Scan *plan, ExplainState *es);
 
87
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
 
88
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
 
89
static void ExplainMemberNodes(List *plans, PlanState **planstates,
 
90
                                   List *ancestors, ExplainState *es);
 
91
static void ExplainSubPlans(List *plans, List *ancestors,
 
92
                                const char *relationship, ExplainState *es);
 
93
static void ExplainProperty(const char *qlabel, const char *value,
 
94
                                bool numeric, ExplainState *es);
 
95
static void ExplainOpenGroup(const char *objtype, const char *labelname,
 
96
                                 bool labeled, ExplainState *es);
 
97
static void ExplainCloseGroup(const char *objtype, const char *labelname,
 
98
                                  bool labeled, ExplainState *es);
 
99
static void ExplainDummyGroup(const char *objtype, const char *labelname,
 
100
                                  ExplainState *es);
 
101
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
 
102
static void ExplainJSONLineEnding(ExplainState *es);
 
103
static void ExplainYAMLLineStarting(ExplainState *es);
 
104
static void escape_json(StringInfo buf, const char *str);
 
105
static void escape_yaml(StringInfo buf, const char *str);
 
106
 
 
107
 
 
108
 
 
109
/*
 
110
 * ExplainQuery -
 
111
 *        execute an EXPLAIN command
 
112
 */
 
113
void
 
114
ExplainQuery(ExplainStmt *stmt, const char *queryString,
 
115
                         ParamListInfo params, DestReceiver *dest)
 
116
{
 
117
        ExplainState es;
 
118
        TupOutputState *tstate;
 
119
        List       *rewritten;
 
120
        ListCell   *lc;
 
121
 
 
122
        /* Initialize ExplainState. */
 
123
        ExplainInitState(&es);
 
124
 
 
125
        /* Parse options list. */
 
126
        foreach(lc, stmt->options)
 
127
        {
 
128
                DefElem    *opt = (DefElem *) lfirst(lc);
 
129
 
 
130
                if (strcmp(opt->defname, "analyze") == 0)
 
131
                        es.analyze = defGetBoolean(opt);
 
132
                else if (strcmp(opt->defname, "verbose") == 0)
 
133
                        es.verbose = defGetBoolean(opt);
 
134
                else if (strcmp(opt->defname, "costs") == 0)
 
135
                        es.costs = defGetBoolean(opt);
 
136
                else if (strcmp(opt->defname, "buffers") == 0)
 
137
                        es.buffers = defGetBoolean(opt);
 
138
                else if (strcmp(opt->defname, "format") == 0)
 
139
                {
 
140
                        char       *p = defGetString(opt);
 
141
 
 
142
                        if (strcmp(p, "text") == 0)
 
143
                                es.format = EXPLAIN_FORMAT_TEXT;
 
144
                        else if (strcmp(p, "xml") == 0)
 
145
                                es.format = EXPLAIN_FORMAT_XML;
 
146
                        else if (strcmp(p, "json") == 0)
 
147
                                es.format = EXPLAIN_FORMAT_JSON;
 
148
                        else if (strcmp(p, "yaml") == 0)
 
149
                                es.format = EXPLAIN_FORMAT_YAML;
 
150
                        else
 
151
                                ereport(ERROR,
 
152
                                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
153
                                errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
 
154
                                           opt->defname, p)));
 
155
                }
 
156
                else
 
157
                        ereport(ERROR,
 
158
                                        (errcode(ERRCODE_SYNTAX_ERROR),
 
159
                                         errmsg("unrecognized EXPLAIN option \"%s\"",
 
160
                                                        opt->defname)));
 
161
        }
 
162
 
 
163
        if (es.buffers && !es.analyze)
 
164
                ereport(ERROR,
 
165
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
166
                                 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
 
167
 
 
168
        /*
 
169
         * Parse analysis was done already, but we still have to run the rule
 
170
         * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
 
171
         * came straight from the parser, or suitable locks were acquired by
 
172
         * plancache.c.
 
173
         *
 
174
         * Because the rewriter and planner tend to scribble on the input, we make
 
175
         * a preliminary copy of the source querytree.  This prevents problems in
 
176
         * the case that the EXPLAIN is in a portal or plpgsql function and is
 
177
         * executed repeatedly.  (See also the same hack in DECLARE CURSOR and
 
178
         * PREPARE.)  XXX FIXME someday.
 
179
         */
 
180
        Assert(IsA(stmt->query, Query));
 
181
        rewritten = QueryRewrite((Query *) copyObject(stmt->query));
 
182
 
 
183
        /* emit opening boilerplate */
 
184
        ExplainBeginOutput(&es);
 
185
 
 
186
        if (rewritten == NIL)
 
187
        {
 
188
                /*
 
189
                 * In the case of an INSTEAD NOTHING, tell at least that.  But in
 
190
                 * non-text format, the output is delimited, so this isn't necessary.
 
191
                 */
 
192
                if (es.format == EXPLAIN_FORMAT_TEXT)
 
193
                        appendStringInfoString(es.str, "Query rewrites to nothing\n");
 
194
        }
 
195
        else
 
196
        {
 
197
                ListCell   *l;
 
198
 
 
199
                /* Explain every plan */
 
200
                foreach(l, rewritten)
 
201
                {
 
202
                        ExplainOneQuery((Query *) lfirst(l), &es, queryString, params);
 
203
 
 
204
                        /* Separate plans with an appropriate separator */
 
205
                        if (lnext(l) != NULL)
 
206
                                ExplainSeparatePlans(&es);
 
207
                }
 
208
        }
 
209
 
 
210
        /* emit closing boilerplate */
 
211
        ExplainEndOutput(&es);
 
212
        Assert(es.indent == 0);
 
213
 
 
214
        /* output tuples */
 
215
        tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
 
216
        if (es.format == EXPLAIN_FORMAT_TEXT)
 
217
                do_text_output_multiline(tstate, es.str->data);
 
218
        else
 
219
                do_text_output_oneline(tstate, es.str->data);
 
220
        end_tup_output(tstate);
 
221
 
 
222
        pfree(es.str->data);
 
223
}
 
224
 
 
225
/*
 
226
 * Initialize ExplainState.
 
227
 */
 
228
void
 
229
ExplainInitState(ExplainState *es)
 
230
{
 
231
        /* Set default options. */
 
232
        memset(es, 0, sizeof(ExplainState));
 
233
        es->costs = true;
 
234
        /* Prepare output buffer. */
 
235
        es->str = makeStringInfo();
 
236
}
 
237
 
 
238
/*
 
239
 * ExplainResultDesc -
 
240
 *        construct the result tupledesc for an EXPLAIN
 
241
 */
 
242
TupleDesc
 
243
ExplainResultDesc(ExplainStmt *stmt)
 
244
{
 
245
        TupleDesc       tupdesc;
 
246
        ListCell   *lc;
 
247
        bool            xml = false;
 
248
 
 
249
        /* Check for XML format option */
 
250
        foreach(lc, stmt->options)
 
251
        {
 
252
                DefElem    *opt = (DefElem *) lfirst(lc);
 
253
 
 
254
                if (strcmp(opt->defname, "format") == 0)
 
255
                {
 
256
                        char       *p = defGetString(opt);
 
257
 
 
258
                        xml = (strcmp(p, "xml") == 0);
 
259
                        /* don't "break", as ExplainQuery will use the last value */
 
260
                }
 
261
        }
 
262
 
 
263
        /* Need a tuple descriptor representing a single TEXT or XML column */
 
264
        tupdesc = CreateTemplateTupleDesc(1, false);
 
265
        TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
 
266
                                           xml ? XMLOID : TEXTOID, -1, 0);
 
267
        return tupdesc;
 
268
}
 
269
 
 
270
/*
 
271
 * ExplainOneQuery -
 
272
 *        print out the execution plan for one Query
 
273
 */
 
274
static void
 
275
ExplainOneQuery(Query *query, ExplainState *es,
 
276
                                const char *queryString, ParamListInfo params)
 
277
{
 
278
        /* planner will not cope with utility statements */
 
279
        if (query->commandType == CMD_UTILITY)
 
280
        {
 
281
                ExplainOneUtility(query->utilityStmt, es, queryString, params);
 
282
                return;
 
283
        }
 
284
 
 
285
        /* if an advisor plugin is present, let it manage things */
 
286
        if (ExplainOneQuery_hook)
 
287
                (*ExplainOneQuery_hook) (query, es, queryString, params);
 
288
        else
 
289
        {
 
290
                PlannedStmt *plan;
 
291
 
 
292
                /* plan the query */
 
293
                plan = pg_plan_query(query, 0, params);
 
294
 
 
295
                /* run it (if needed) and produce output */
 
296
                ExplainOnePlan(plan, es, queryString, params);
 
297
        }
 
298
}
 
299
 
 
300
/*
 
301
 * ExplainOneUtility -
 
302
 *        print out the execution plan for one utility statement
 
303
 *        (In general, utility statements don't have plans, but there are some
 
304
 *        we treat as special cases)
 
305
 *
 
306
 * This is exported because it's called back from prepare.c in the
 
307
 * EXPLAIN EXECUTE case
 
308
 */
 
309
void
 
310
ExplainOneUtility(Node *utilityStmt, ExplainState *es,
 
311
                                  const char *queryString, ParamListInfo params)
 
312
{
 
313
        if (utilityStmt == NULL)
 
314
                return;
 
315
 
 
316
        if (IsA(utilityStmt, ExecuteStmt))
 
317
                ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es,
 
318
                                                        queryString, params);
 
319
        else if (IsA(utilityStmt, NotifyStmt))
 
320
        {
 
321
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
322
                        appendStringInfoString(es->str, "NOTIFY\n");
 
323
                else
 
324
                        ExplainDummyGroup("Notify", NULL, es);
 
325
        }
 
326
        else
 
327
        {
 
328
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
329
                        appendStringInfoString(es->str,
 
330
                                                          "Utility statements have no plan structure\n");
 
331
                else
 
332
                        ExplainDummyGroup("Utility Statement", NULL, es);
 
333
        }
 
334
}
 
335
 
 
336
/*
 
337
 * ExplainOnePlan -
 
338
 *              given a planned query, execute it if needed, and then print
 
339
 *              EXPLAIN output
 
340
 *
 
341
 * Since we ignore any DeclareCursorStmt that might be attached to the query,
 
342
 * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
 
343
 * query.  This is different from pre-8.3 behavior but seems more useful than
 
344
 * not running the query.  No cursor will be created, however.
 
345
 *
 
346
 * This is exported because it's called back from prepare.c in the
 
347
 * EXPLAIN EXECUTE case, and because an index advisor plugin would need
 
348
 * to call it.
 
349
 */
 
350
void
 
351
ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
 
352
                           const char *queryString, ParamListInfo params)
 
353
{
 
354
        QueryDesc  *queryDesc;
 
355
        instr_time      starttime;
 
356
        double          totaltime = 0;
 
357
        int                     eflags;
 
358
        int                     instrument_option = 0;
 
359
 
 
360
        if (es->analyze)
 
361
                instrument_option |= INSTRUMENT_TIMER;
 
362
        if (es->buffers)
 
363
                instrument_option |= INSTRUMENT_BUFFERS;
 
364
 
 
365
        INSTR_TIME_SET_CURRENT(starttime);
 
366
 
 
367
        /*
 
368
         * Use a snapshot with an updated command ID to ensure this query sees
 
369
         * results of any previously executed queries.
 
370
         */
 
371
        PushCopiedSnapshot(GetActiveSnapshot());
 
372
        UpdateActiveSnapshotCommandId();
 
373
 
 
374
        /* Create a QueryDesc requesting no output */
 
375
        queryDesc = CreateQueryDesc(plannedstmt, queryString,
 
376
                                                                GetActiveSnapshot(), InvalidSnapshot,
 
377
                                                                None_Receiver, params, instrument_option);
 
378
 
 
379
        /* Select execution options */
 
380
        if (es->analyze)
 
381
                eflags = 0;                             /* default run-to-completion flags */
 
382
        else
 
383
                eflags = EXEC_FLAG_EXPLAIN_ONLY;
 
384
 
 
385
        /* call ExecutorStart to prepare the plan for execution */
 
386
        ExecutorStart(queryDesc, eflags);
 
387
 
 
388
        /* Execute the plan for statistics if asked for */
 
389
        if (es->analyze)
 
390
        {
 
391
                /* run the plan */
 
392
                ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 
393
 
 
394
                /* run cleanup too */
 
395
                ExecutorFinish(queryDesc);
 
396
 
 
397
                /* We can't run ExecutorEnd 'till we're done printing the stats... */
 
398
                totaltime += elapsed_time(&starttime);
 
399
        }
 
400
 
 
401
        ExplainOpenGroup("Query", NULL, true, es);
 
402
 
 
403
        /* Create textual dump of plan tree */
 
404
        ExplainPrintPlan(es, queryDesc);
 
405
 
 
406
        /* Print info about runtime of triggers */
 
407
        if (es->analyze)
 
408
        {
 
409
                ResultRelInfo *rInfo;
 
410
                bool            show_relname;
 
411
                int                     numrels = queryDesc->estate->es_num_result_relations;
 
412
                List       *targrels = queryDesc->estate->es_trig_target_relations;
 
413
                int                     nr;
 
414
                ListCell   *l;
 
415
 
 
416
                ExplainOpenGroup("Triggers", "Triggers", false, es);
 
417
 
 
418
                show_relname = (numrels > 1 || targrels != NIL);
 
419
                rInfo = queryDesc->estate->es_result_relations;
 
420
                for (nr = 0; nr < numrels; rInfo++, nr++)
 
421
                        report_triggers(rInfo, show_relname, es);
 
422
 
 
423
                foreach(l, targrels)
 
424
                {
 
425
                        rInfo = (ResultRelInfo *) lfirst(l);
 
426
                        report_triggers(rInfo, show_relname, es);
 
427
                }
 
428
 
 
429
                ExplainCloseGroup("Triggers", "Triggers", false, es);
 
430
        }
 
431
 
 
432
        /*
 
433
         * Close down the query and free resources.  Include time for this in the
 
434
         * total runtime (although it should be pretty minimal).
 
435
         */
 
436
        INSTR_TIME_SET_CURRENT(starttime);
 
437
 
 
438
        ExecutorEnd(queryDesc);
 
439
 
 
440
        FreeQueryDesc(queryDesc);
 
441
 
 
442
        PopActiveSnapshot();
 
443
 
 
444
        /* We need a CCI just in case query expanded to multiple plans */
 
445
        if (es->analyze)
 
446
                CommandCounterIncrement();
 
447
 
 
448
        totaltime += elapsed_time(&starttime);
 
449
 
 
450
        if (es->analyze)
 
451
        {
 
452
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
453
                        appendStringInfo(es->str, "Total runtime: %.3f ms\n",
 
454
                                                         1000.0 * totaltime);
 
455
                else
 
456
                        ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime,
 
457
                                                                 3, es);
 
458
        }
 
459
 
 
460
        ExplainCloseGroup("Query", NULL, true, es);
 
461
}
 
462
 
 
463
/*
 
464
 * ExplainPrintPlan -
 
465
 *        convert a QueryDesc's plan tree to text and append it to es->str
 
466
 *
 
467
 * The caller should have set up the options fields of *es, as well as
 
468
 * initializing the output buffer es->str.      Other fields in *es are
 
469
 * initialized here.
 
470
 *
 
471
 * NB: will not work on utility statements
 
472
 */
 
473
void
 
474
ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
 
475
{
 
476
        Assert(queryDesc->plannedstmt != NULL);
 
477
        es->pstmt = queryDesc->plannedstmt;
 
478
        es->rtable = queryDesc->plannedstmt->rtable;
 
479
        ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
 
480
}
 
481
 
 
482
/*
 
483
 * ExplainQueryText -
 
484
 *        add a "Query Text" node that contains the actual text of the query
 
485
 *
 
486
 * The caller should have set up the options fields of *es, as well as
 
487
 * initializing the output buffer es->str.
 
488
 *
 
489
 */
 
490
void
 
491
ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
 
492
{
 
493
        if (queryDesc->sourceText)
 
494
                ExplainPropertyText("Query Text", queryDesc->sourceText, es);
 
495
}
 
496
 
 
497
/*
 
498
 * report_triggers -
 
499
 *              report execution stats for a single relation's triggers
 
500
 */
 
501
static void
 
502
report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
 
503
{
 
504
        int                     nt;
 
505
 
 
506
        if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
 
507
                return;
 
508
        for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
 
509
        {
 
510
                Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
 
511
                Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
 
512
                char       *relname;
 
513
                char       *conname = NULL;
 
514
 
 
515
                /* Must clean up instrumentation state */
 
516
                InstrEndLoop(instr);
 
517
 
 
518
                /*
 
519
                 * We ignore triggers that were never invoked; they likely aren't
 
520
                 * relevant to the current query type.
 
521
                 */
 
522
                if (instr->ntuples == 0)
 
523
                        continue;
 
524
 
 
525
                ExplainOpenGroup("Trigger", NULL, true, es);
 
526
 
 
527
                relname = RelationGetRelationName(rInfo->ri_RelationDesc);
 
528
                if (OidIsValid(trig->tgconstraint))
 
529
                        conname = get_constraint_name(trig->tgconstraint);
 
530
 
 
531
                /*
 
532
                 * In text format, we avoid printing both the trigger name and the
 
533
                 * constraint name unless VERBOSE is specified.  In non-text formats
 
534
                 * we just print everything.
 
535
                 */
 
536
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
537
                {
 
538
                        if (es->verbose || conname == NULL)
 
539
                                appendStringInfo(es->str, "Trigger %s", trig->tgname);
 
540
                        else
 
541
                                appendStringInfoString(es->str, "Trigger");
 
542
                        if (conname)
 
543
                                appendStringInfo(es->str, " for constraint %s", conname);
 
544
                        if (show_relname)
 
545
                                appendStringInfo(es->str, " on %s", relname);
 
546
                        appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
 
547
                                                         1000.0 * instr->total, instr->ntuples);
 
548
                }
 
549
                else
 
550
                {
 
551
                        ExplainPropertyText("Trigger Name", trig->tgname, es);
 
552
                        if (conname)
 
553
                                ExplainPropertyText("Constraint Name", conname, es);
 
554
                        ExplainPropertyText("Relation", relname, es);
 
555
                        ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
 
556
                        ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
 
557
                }
 
558
 
 
559
                if (conname)
 
560
                        pfree(conname);
 
561
 
 
562
                ExplainCloseGroup("Trigger", NULL, true, es);
 
563
        }
 
564
}
 
565
 
 
566
/* Compute elapsed time in seconds since given timestamp */
 
567
static double
 
568
elapsed_time(instr_time *starttime)
 
569
{
 
570
        instr_time      endtime;
 
571
 
 
572
        INSTR_TIME_SET_CURRENT(endtime);
 
573
        INSTR_TIME_SUBTRACT(endtime, *starttime);
 
574
        return INSTR_TIME_GET_DOUBLE(endtime);
 
575
}
 
576
 
 
577
/*
 
578
 * ExplainNode -
 
579
 *        Appends a description of a plan tree to es->str
 
580
 *
 
581
 * planstate points to the executor state node for the current plan node.
 
582
 * We need to work from a PlanState node, not just a Plan node, in order to
 
583
 * get at the instrumentation data (if any) as well as the list of subplans.
 
584
 *
 
585
 * ancestors is a list of parent PlanState nodes, most-closely-nested first.
 
586
 * These are needed in order to interpret PARAM_EXEC Params.
 
587
 *
 
588
 * relationship describes the relationship of this plan node to its parent
 
589
 * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
 
590
 * optional name to be attached to the node.
 
591
 *
 
592
 * In text format, es->indent is controlled in this function since we only
 
593
 * want it to change at plan-node boundaries.  In non-text formats, es->indent
 
594
 * corresponds to the nesting depth of logical output groups, and therefore
 
595
 * is controlled by ExplainOpenGroup/ExplainCloseGroup.
 
596
 */
 
597
static void
 
598
ExplainNode(PlanState *planstate, List *ancestors,
 
599
                        const char *relationship, const char *plan_name,
 
600
                        ExplainState *es)
 
601
{
 
602
        Plan       *plan = planstate->plan;
 
603
        const char *pname;                      /* node type name for text output */
 
604
        const char *sname;                      /* node type name for non-text output */
 
605
        const char *strategy = NULL;
 
606
        const char *operation = NULL;
 
607
        int                     save_indent = es->indent;
 
608
        bool            haschildren;
 
609
 
 
610
        switch (nodeTag(plan))
 
611
        {
 
612
                case T_Result:
 
613
                        pname = sname = "Result";
 
614
                        break;
 
615
                case T_ModifyTable:
 
616
                        sname = "ModifyTable";
 
617
                        switch (((ModifyTable *) plan)->operation)
 
618
                        {
 
619
                                case CMD_INSERT:
 
620
                                        pname = operation = "Insert";
 
621
                                        break;
 
622
                                case CMD_UPDATE:
 
623
                                        pname = operation = "Update";
 
624
                                        break;
 
625
                                case CMD_DELETE:
 
626
                                        pname = operation = "Delete";
 
627
                                        break;
 
628
                                default:
 
629
                                        pname = "???";
 
630
                                        break;
 
631
                        }
 
632
                        break;
 
633
                case T_Append:
 
634
                        pname = sname = "Append";
 
635
                        break;
 
636
                case T_MergeAppend:
 
637
                        pname = sname = "Merge Append";
 
638
                        break;
 
639
                case T_RecursiveUnion:
 
640
                        pname = sname = "Recursive Union";
 
641
                        break;
 
642
                case T_BitmapAnd:
 
643
                        pname = sname = "BitmapAnd";
 
644
                        break;
 
645
                case T_BitmapOr:
 
646
                        pname = sname = "BitmapOr";
 
647
                        break;
 
648
                case T_NestLoop:
 
649
                        pname = sname = "Nested Loop";
 
650
                        break;
 
651
                case T_MergeJoin:
 
652
                        pname = "Merge";        /* "Join" gets added by jointype switch */
 
653
                        sname = "Merge Join";
 
654
                        break;
 
655
                case T_HashJoin:
 
656
                        pname = "Hash";         /* "Join" gets added by jointype switch */
 
657
                        sname = "Hash Join";
 
658
                        break;
 
659
                case T_SeqScan:
 
660
                        pname = sname = "Seq Scan";
 
661
                        break;
 
662
                case T_IndexScan:
 
663
                        pname = sname = "Index Scan";
 
664
                        break;
 
665
                case T_BitmapIndexScan:
 
666
                        pname = sname = "Bitmap Index Scan";
 
667
                        break;
 
668
                case T_BitmapHeapScan:
 
669
                        pname = sname = "Bitmap Heap Scan";
 
670
                        break;
 
671
                case T_TidScan:
 
672
                        pname = sname = "Tid Scan";
 
673
                        break;
 
674
                case T_SubqueryScan:
 
675
                        pname = sname = "Subquery Scan";
 
676
                        break;
 
677
                case T_FunctionScan:
 
678
                        pname = sname = "Function Scan";
 
679
                        break;
 
680
                case T_ValuesScan:
 
681
                        pname = sname = "Values Scan";
 
682
                        break;
 
683
                case T_CteScan:
 
684
                        pname = sname = "CTE Scan";
 
685
                        break;
 
686
                case T_WorkTableScan:
 
687
                        pname = sname = "WorkTable Scan";
 
688
                        break;
 
689
                case T_ForeignScan:
 
690
                        pname = sname = "Foreign Scan";
 
691
                        break;
 
692
                case T_Material:
 
693
                        pname = sname = "Materialize";
 
694
                        break;
 
695
                case T_Sort:
 
696
                        pname = sname = "Sort";
 
697
                        break;
 
698
                case T_Group:
 
699
                        pname = sname = "Group";
 
700
                        break;
 
701
                case T_Agg:
 
702
                        sname = "Aggregate";
 
703
                        switch (((Agg *) plan)->aggstrategy)
 
704
                        {
 
705
                                case AGG_PLAIN:
 
706
                                        pname = "Aggregate";
 
707
                                        strategy = "Plain";
 
708
                                        break;
 
709
                                case AGG_SORTED:
 
710
                                        pname = "GroupAggregate";
 
711
                                        strategy = "Sorted";
 
712
                                        break;
 
713
                                case AGG_HASHED:
 
714
                                        pname = "HashAggregate";
 
715
                                        strategy = "Hashed";
 
716
                                        break;
 
717
                                default:
 
718
                                        pname = "Aggregate ???";
 
719
                                        strategy = "???";
 
720
                                        break;
 
721
                        }
 
722
                        break;
 
723
                case T_WindowAgg:
 
724
                        pname = sname = "WindowAgg";
 
725
                        break;
 
726
                case T_Unique:
 
727
                        pname = sname = "Unique";
 
728
                        break;
 
729
                case T_SetOp:
 
730
                        sname = "SetOp";
 
731
                        switch (((SetOp *) plan)->strategy)
 
732
                        {
 
733
                                case SETOP_SORTED:
 
734
                                        pname = "SetOp";
 
735
                                        strategy = "Sorted";
 
736
                                        break;
 
737
                                case SETOP_HASHED:
 
738
                                        pname = "HashSetOp";
 
739
                                        strategy = "Hashed";
 
740
                                        break;
 
741
                                default:
 
742
                                        pname = "SetOp ???";
 
743
                                        strategy = "???";
 
744
                                        break;
 
745
                        }
 
746
                        break;
 
747
                case T_LockRows:
 
748
                        pname = sname = "LockRows";
 
749
                        break;
 
750
                case T_Limit:
 
751
                        pname = sname = "Limit";
 
752
                        break;
 
753
                case T_Hash:
 
754
                        pname = sname = "Hash";
 
755
                        break;
 
756
                default:
 
757
                        pname = sname = "???";
 
758
                        break;
 
759
        }
 
760
 
 
761
        ExplainOpenGroup("Plan",
 
762
                                         relationship ? NULL : "Plan",
 
763
                                         true, es);
 
764
 
 
765
        if (es->format == EXPLAIN_FORMAT_TEXT)
 
766
        {
 
767
                if (plan_name)
 
768
                {
 
769
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
770
                        appendStringInfo(es->str, "%s\n", plan_name);
 
771
                        es->indent++;
 
772
                }
 
773
                if (es->indent)
 
774
                {
 
775
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
776
                        appendStringInfoString(es->str, "->  ");
 
777
                        es->indent += 2;
 
778
                }
 
779
                appendStringInfoString(es->str, pname);
 
780
                es->indent++;
 
781
        }
 
782
        else
 
783
        {
 
784
                ExplainPropertyText("Node Type", sname, es);
 
785
                if (strategy)
 
786
                        ExplainPropertyText("Strategy", strategy, es);
 
787
                if (operation)
 
788
                        ExplainPropertyText("Operation", operation, es);
 
789
                if (relationship)
 
790
                        ExplainPropertyText("Parent Relationship", relationship, es);
 
791
                if (plan_name)
 
792
                        ExplainPropertyText("Subplan Name", plan_name, es);
 
793
        }
 
794
 
 
795
        switch (nodeTag(plan))
 
796
        {
 
797
                case T_IndexScan:
 
798
                        {
 
799
                                IndexScan  *indexscan = (IndexScan *) plan;
 
800
                                const char *indexname =
 
801
                                explain_get_index_name(indexscan->indexid);
 
802
 
 
803
                                if (es->format == EXPLAIN_FORMAT_TEXT)
 
804
                                {
 
805
                                        if (ScanDirectionIsBackward(indexscan->indexorderdir))
 
806
                                                appendStringInfoString(es->str, " Backward");
 
807
                                        appendStringInfo(es->str, " using %s", indexname);
 
808
                                }
 
809
                                else
 
810
                                {
 
811
                                        const char *scandir;
 
812
 
 
813
                                        switch (indexscan->indexorderdir)
 
814
                                        {
 
815
                                                case BackwardScanDirection:
 
816
                                                        scandir = "Backward";
 
817
                                                        break;
 
818
                                                case NoMovementScanDirection:
 
819
                                                        scandir = "NoMovement";
 
820
                                                        break;
 
821
                                                case ForwardScanDirection:
 
822
                                                        scandir = "Forward";
 
823
                                                        break;
 
824
                                                default:
 
825
                                                        scandir = "???";
 
826
                                                        break;
 
827
                                        }
 
828
                                        ExplainPropertyText("Scan Direction", scandir, es);
 
829
                                        ExplainPropertyText("Index Name", indexname, es);
 
830
                                }
 
831
                        }
 
832
                        /* FALL THRU */
 
833
                case T_SeqScan:
 
834
                case T_BitmapHeapScan:
 
835
                case T_TidScan:
 
836
                case T_SubqueryScan:
 
837
                case T_FunctionScan:
 
838
                case T_ValuesScan:
 
839
                case T_CteScan:
 
840
                case T_WorkTableScan:
 
841
                case T_ForeignScan:
 
842
                        ExplainScanTarget((Scan *) plan, es);
 
843
                        break;
 
844
                case T_BitmapIndexScan:
 
845
                        {
 
846
                                BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
 
847
                                const char *indexname =
 
848
                                explain_get_index_name(bitmapindexscan->indexid);
 
849
 
 
850
                                if (es->format == EXPLAIN_FORMAT_TEXT)
 
851
                                        appendStringInfo(es->str, " on %s", indexname);
 
852
                                else
 
853
                                        ExplainPropertyText("Index Name", indexname, es);
 
854
                        }
 
855
                        break;
 
856
                case T_ModifyTable:
 
857
                        ExplainModifyTarget((ModifyTable *) plan, es);
 
858
                        break;
 
859
                case T_NestLoop:
 
860
                case T_MergeJoin:
 
861
                case T_HashJoin:
 
862
                        {
 
863
                                const char *jointype;
 
864
 
 
865
                                switch (((Join *) plan)->jointype)
 
866
                                {
 
867
                                        case JOIN_INNER:
 
868
                                                jointype = "Inner";
 
869
                                                break;
 
870
                                        case JOIN_LEFT:
 
871
                                                jointype = "Left";
 
872
                                                break;
 
873
                                        case JOIN_FULL:
 
874
                                                jointype = "Full";
 
875
                                                break;
 
876
                                        case JOIN_RIGHT:
 
877
                                                jointype = "Right";
 
878
                                                break;
 
879
                                        case JOIN_SEMI:
 
880
                                                jointype = "Semi";
 
881
                                                break;
 
882
                                        case JOIN_ANTI:
 
883
                                                jointype = "Anti";
 
884
                                                break;
 
885
                                        default:
 
886
                                                jointype = "???";
 
887
                                                break;
 
888
                                }
 
889
                                if (es->format == EXPLAIN_FORMAT_TEXT)
 
890
                                {
 
891
                                        /*
 
892
                                         * For historical reasons, the join type is interpolated
 
893
                                         * into the node type name...
 
894
                                         */
 
895
                                        if (((Join *) plan)->jointype != JOIN_INNER)
 
896
                                                appendStringInfo(es->str, " %s Join", jointype);
 
897
                                        else if (!IsA(plan, NestLoop))
 
898
                                                appendStringInfo(es->str, " Join");
 
899
                                }
 
900
                                else
 
901
                                        ExplainPropertyText("Join Type", jointype, es);
 
902
                        }
 
903
                        break;
 
904
                case T_SetOp:
 
905
                        {
 
906
                                const char *setopcmd;
 
907
 
 
908
                                switch (((SetOp *) plan)->cmd)
 
909
                                {
 
910
                                        case SETOPCMD_INTERSECT:
 
911
                                                setopcmd = "Intersect";
 
912
                                                break;
 
913
                                        case SETOPCMD_INTERSECT_ALL:
 
914
                                                setopcmd = "Intersect All";
 
915
                                                break;
 
916
                                        case SETOPCMD_EXCEPT:
 
917
                                                setopcmd = "Except";
 
918
                                                break;
 
919
                                        case SETOPCMD_EXCEPT_ALL:
 
920
                                                setopcmd = "Except All";
 
921
                                                break;
 
922
                                        default:
 
923
                                                setopcmd = "???";
 
924
                                                break;
 
925
                                }
 
926
                                if (es->format == EXPLAIN_FORMAT_TEXT)
 
927
                                        appendStringInfo(es->str, " %s", setopcmd);
 
928
                                else
 
929
                                        ExplainPropertyText("Command", setopcmd, es);
 
930
                        }
 
931
                        break;
 
932
                default:
 
933
                        break;
 
934
        }
 
935
 
 
936
        if (es->costs)
 
937
        {
 
938
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
939
                {
 
940
                        appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
 
941
                                                         plan->startup_cost, plan->total_cost,
 
942
                                                         plan->plan_rows, plan->plan_width);
 
943
                }
 
944
                else
 
945
                {
 
946
                        ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
 
947
                        ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
 
948
                        ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
 
949
                        ExplainPropertyInteger("Plan Width", plan->plan_width, es);
 
950
                }
 
951
        }
 
952
 
 
953
        /*
 
954
         * We have to forcibly clean up the instrumentation state because we
 
955
         * haven't done ExecutorEnd yet.  This is pretty grotty ...
 
956
         */
 
957
        if (planstate->instrument)
 
958
                InstrEndLoop(planstate->instrument);
 
959
 
 
960
        if (planstate->instrument && planstate->instrument->nloops > 0)
 
961
        {
 
962
                double          nloops = planstate->instrument->nloops;
 
963
                double          startup_sec = 1000.0 * planstate->instrument->startup / nloops;
 
964
                double          total_sec = 1000.0 * planstate->instrument->total / nloops;
 
965
                double          rows = planstate->instrument->ntuples / nloops;
 
966
 
 
967
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
968
                {
 
969
                        appendStringInfo(es->str,
 
970
                                                         " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
 
971
                                                         startup_sec, total_sec, rows, nloops);
 
972
                }
 
973
                else
 
974
                {
 
975
                        ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
 
976
                        ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
 
977
                        ExplainPropertyFloat("Actual Rows", rows, 0, es);
 
978
                        ExplainPropertyFloat("Actual Loops", nloops, 0, es);
 
979
                }
 
980
        }
 
981
        else if (es->analyze)
 
982
        {
 
983
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
984
                        appendStringInfo(es->str, " (never executed)");
 
985
                else
 
986
                {
 
987
                        ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
 
988
                        ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
 
989
                        ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
 
990
                        ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
 
991
                }
 
992
        }
 
993
 
 
994
        /* in text format, first line ends here */
 
995
        if (es->format == EXPLAIN_FORMAT_TEXT)
 
996
                appendStringInfoChar(es->str, '\n');
 
997
 
 
998
        /* target list */
 
999
        if (es->verbose)
 
1000
                show_plan_tlist(planstate, ancestors, es);
 
1001
 
 
1002
        /* quals, sort keys, etc */
 
1003
        switch (nodeTag(plan))
 
1004
        {
 
1005
                case T_IndexScan:
 
1006
                        show_scan_qual(((IndexScan *) plan)->indexqualorig,
 
1007
                                                   "Index Cond", planstate, ancestors, es);
 
1008
                        show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
 
1009
                                                   "Order By", planstate, ancestors, es);
 
1010
                        show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1011
                        break;
 
1012
                case T_BitmapIndexScan:
 
1013
                        show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
 
1014
                                                   "Index Cond", planstate, ancestors, es);
 
1015
                        break;
 
1016
                case T_BitmapHeapScan:
 
1017
                        show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
 
1018
                                                   "Recheck Cond", planstate, ancestors, es);
 
1019
                        /* FALL THRU */
 
1020
                case T_SeqScan:
 
1021
                case T_ValuesScan:
 
1022
                case T_CteScan:
 
1023
                case T_WorkTableScan:
 
1024
                case T_SubqueryScan:
 
1025
                        show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1026
                        break;
 
1027
                case T_FunctionScan:
 
1028
                        if (es->verbose)
 
1029
                                show_expression(((FunctionScan *) plan)->funcexpr,
 
1030
                                                                "Function Call", planstate, ancestors,
 
1031
                                                                es->verbose, es);
 
1032
                        show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1033
                        break;
 
1034
                case T_TidScan:
 
1035
                        {
 
1036
                                /*
 
1037
                                 * The tidquals list has OR semantics, so be sure to show it
 
1038
                                 * as an OR condition.
 
1039
                                 */
 
1040
                                List       *tidquals = ((TidScan *) plan)->tidquals;
 
1041
 
 
1042
                                if (list_length(tidquals) > 1)
 
1043
                                        tidquals = list_make1(make_orclause(tidquals));
 
1044
                                show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
 
1045
                                show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1046
                        }
 
1047
                        break;
 
1048
                case T_ForeignScan:
 
1049
                        show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1050
                        show_foreignscan_info((ForeignScanState *) planstate, es);
 
1051
                        break;
 
1052
                case T_NestLoop:
 
1053
                        show_upper_qual(((NestLoop *) plan)->join.joinqual,
 
1054
                                                        "Join Filter", planstate, ancestors, es);
 
1055
                        show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1056
                        break;
 
1057
                case T_MergeJoin:
 
1058
                        show_upper_qual(((MergeJoin *) plan)->mergeclauses,
 
1059
                                                        "Merge Cond", planstate, ancestors, es);
 
1060
                        show_upper_qual(((MergeJoin *) plan)->join.joinqual,
 
1061
                                                        "Join Filter", planstate, ancestors, es);
 
1062
                        show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1063
                        break;
 
1064
                case T_HashJoin:
 
1065
                        show_upper_qual(((HashJoin *) plan)->hashclauses,
 
1066
                                                        "Hash Cond", planstate, ancestors, es);
 
1067
                        show_upper_qual(((HashJoin *) plan)->join.joinqual,
 
1068
                                                        "Join Filter", planstate, ancestors, es);
 
1069
                        show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1070
                        break;
 
1071
                case T_Agg:
 
1072
                case T_Group:
 
1073
                        show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1074
                        break;
 
1075
                case T_Sort:
 
1076
                        show_sort_keys((SortState *) planstate, ancestors, es);
 
1077
                        show_sort_info((SortState *) planstate, es);
 
1078
                        break;
 
1079
                case T_MergeAppend:
 
1080
                        show_merge_append_keys((MergeAppendState *) planstate,
 
1081
                                                                   ancestors, es);
 
1082
                        break;
 
1083
                case T_Result:
 
1084
                        show_upper_qual((List *) ((Result *) plan)->resconstantqual,
 
1085
                                                        "One-Time Filter", planstate, ancestors, es);
 
1086
                        show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
 
1087
                        break;
 
1088
                case T_Hash:
 
1089
                        show_hash_info((HashState *) planstate, es);
 
1090
                        break;
 
1091
                default:
 
1092
                        break;
 
1093
        }
 
1094
 
 
1095
        /* Show buffer usage */
 
1096
        if (es->buffers)
 
1097
        {
 
1098
                const BufferUsage *usage = &planstate->instrument->bufusage;
 
1099
 
 
1100
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
1101
                {
 
1102
                        bool            has_shared = (usage->shared_blks_hit > 0 ||
 
1103
                                                                          usage->shared_blks_read > 0 ||
 
1104
                                                                          usage->shared_blks_written);
 
1105
                        bool            has_local = (usage->local_blks_hit > 0 ||
 
1106
                                                                         usage->local_blks_read > 0 ||
 
1107
                                                                         usage->local_blks_written);
 
1108
                        bool            has_temp = (usage->temp_blks_read > 0 ||
 
1109
                                                                        usage->temp_blks_written);
 
1110
 
 
1111
                        /* Show only positive counter values. */
 
1112
                        if (has_shared || has_local || has_temp)
 
1113
                        {
 
1114
                                appendStringInfoSpaces(es->str, es->indent * 2);
 
1115
                                appendStringInfoString(es->str, "Buffers:");
 
1116
 
 
1117
                                if (has_shared)
 
1118
                                {
 
1119
                                        appendStringInfoString(es->str, " shared");
 
1120
                                        if (usage->shared_blks_hit > 0)
 
1121
                                                appendStringInfo(es->str, " hit=%ld",
 
1122
                                                                                 usage->shared_blks_hit);
 
1123
                                        if (usage->shared_blks_read > 0)
 
1124
                                                appendStringInfo(es->str, " read=%ld",
 
1125
                                                                                 usage->shared_blks_read);
 
1126
                                        if (usage->shared_blks_written > 0)
 
1127
                                                appendStringInfo(es->str, " written=%ld",
 
1128
                                                                                 usage->shared_blks_written);
 
1129
                                        if (has_local || has_temp)
 
1130
                                                appendStringInfoChar(es->str, ',');
 
1131
                                }
 
1132
                                if (has_local)
 
1133
                                {
 
1134
                                        appendStringInfoString(es->str, " local");
 
1135
                                        if (usage->local_blks_hit > 0)
 
1136
                                                appendStringInfo(es->str, " hit=%ld",
 
1137
                                                                                 usage->local_blks_hit);
 
1138
                                        if (usage->local_blks_read > 0)
 
1139
                                                appendStringInfo(es->str, " read=%ld",
 
1140
                                                                                 usage->local_blks_read);
 
1141
                                        if (usage->local_blks_written > 0)
 
1142
                                                appendStringInfo(es->str, " written=%ld",
 
1143
                                                                                 usage->local_blks_written);
 
1144
                                        if (has_temp)
 
1145
                                                appendStringInfoChar(es->str, ',');
 
1146
                                }
 
1147
                                if (has_temp)
 
1148
                                {
 
1149
                                        appendStringInfoString(es->str, " temp");
 
1150
                                        if (usage->temp_blks_read > 0)
 
1151
                                                appendStringInfo(es->str, " read=%ld",
 
1152
                                                                                 usage->temp_blks_read);
 
1153
                                        if (usage->temp_blks_written > 0)
 
1154
                                                appendStringInfo(es->str, " written=%ld",
 
1155
                                                                                 usage->temp_blks_written);
 
1156
                                }
 
1157
                                appendStringInfoChar(es->str, '\n');
 
1158
                        }
 
1159
                }
 
1160
                else
 
1161
                {
 
1162
                        ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
 
1163
                        ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
 
1164
                        ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
 
1165
                        ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
 
1166
                        ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
 
1167
                        ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
 
1168
                        ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
 
1169
                        ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
 
1170
                }
 
1171
        }
 
1172
 
 
1173
        /* Get ready to display the child plans */
 
1174
        haschildren = planstate->initPlan ||
 
1175
                outerPlanState(planstate) ||
 
1176
                innerPlanState(planstate) ||
 
1177
                IsA(plan, ModifyTable) ||
 
1178
                IsA(plan, Append) ||
 
1179
                IsA(plan, MergeAppend) ||
 
1180
                IsA(plan, BitmapAnd) ||
 
1181
                IsA(plan, BitmapOr) ||
 
1182
                IsA(plan, SubqueryScan) ||
 
1183
                planstate->subPlan;
 
1184
        if (haschildren)
 
1185
        {
 
1186
                ExplainOpenGroup("Plans", "Plans", false, es);
 
1187
                /* Pass current PlanState as head of ancestors list for children */
 
1188
                ancestors = lcons(planstate, ancestors);
 
1189
        }
 
1190
 
 
1191
        /* initPlan-s */
 
1192
        if (planstate->initPlan)
 
1193
                ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
 
1194
 
 
1195
        /* lefttree */
 
1196
        if (outerPlanState(planstate))
 
1197
                ExplainNode(outerPlanState(planstate), ancestors,
 
1198
                                        "Outer", NULL, es);
 
1199
 
 
1200
        /* righttree */
 
1201
        if (innerPlanState(planstate))
 
1202
                ExplainNode(innerPlanState(planstate), ancestors,
 
1203
                                        "Inner", NULL, es);
 
1204
 
 
1205
        /* special child plans */
 
1206
        switch (nodeTag(plan))
 
1207
        {
 
1208
                case T_ModifyTable:
 
1209
                        ExplainMemberNodes(((ModifyTable *) plan)->plans,
 
1210
                                                           ((ModifyTableState *) planstate)->mt_plans,
 
1211
                                                           ancestors, es);
 
1212
                        break;
 
1213
                case T_Append:
 
1214
                        ExplainMemberNodes(((Append *) plan)->appendplans,
 
1215
                                                           ((AppendState *) planstate)->appendplans,
 
1216
                                                           ancestors, es);
 
1217
                        break;
 
1218
                case T_MergeAppend:
 
1219
                        ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
 
1220
                                                           ((MergeAppendState *) planstate)->mergeplans,
 
1221
                                                           ancestors, es);
 
1222
                        break;
 
1223
                case T_BitmapAnd:
 
1224
                        ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
 
1225
                                                           ((BitmapAndState *) planstate)->bitmapplans,
 
1226
                                                           ancestors, es);
 
1227
                        break;
 
1228
                case T_BitmapOr:
 
1229
                        ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
 
1230
                                                           ((BitmapOrState *) planstate)->bitmapplans,
 
1231
                                                           ancestors, es);
 
1232
                        break;
 
1233
                case T_SubqueryScan:
 
1234
                        ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
 
1235
                                                "Subquery", NULL, es);
 
1236
                        break;
 
1237
                default:
 
1238
                        break;
 
1239
        }
 
1240
 
 
1241
        /* subPlan-s */
 
1242
        if (planstate->subPlan)
 
1243
                ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
 
1244
 
 
1245
        /* end of child plans */
 
1246
        if (haschildren)
 
1247
        {
 
1248
                ancestors = list_delete_first(ancestors);
 
1249
                ExplainCloseGroup("Plans", "Plans", false, es);
 
1250
        }
 
1251
 
 
1252
        /* in text format, undo whatever indentation we added */
 
1253
        if (es->format == EXPLAIN_FORMAT_TEXT)
 
1254
                es->indent = save_indent;
 
1255
 
 
1256
        ExplainCloseGroup("Plan",
 
1257
                                          relationship ? NULL : "Plan",
 
1258
                                          true, es);
 
1259
}
 
1260
 
 
1261
/*
 
1262
 * Show the targetlist of a plan node
 
1263
 */
 
1264
static void
 
1265
show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
 
1266
{
 
1267
        Plan       *plan = planstate->plan;
 
1268
        List       *context;
 
1269
        List       *result = NIL;
 
1270
        bool            useprefix;
 
1271
        ListCell   *lc;
 
1272
 
 
1273
        /* No work if empty tlist (this occurs eg in bitmap indexscans) */
 
1274
        if (plan->targetlist == NIL)
 
1275
                return;
 
1276
        /* The tlist of an Append isn't real helpful, so suppress it */
 
1277
        if (IsA(plan, Append))
 
1278
                return;
 
1279
        /* Likewise for MergeAppend and RecursiveUnion */
 
1280
        if (IsA(plan, MergeAppend))
 
1281
                return;
 
1282
        if (IsA(plan, RecursiveUnion))
 
1283
                return;
 
1284
 
 
1285
        /* Set up deparsing context */
 
1286
        context = deparse_context_for_planstate((Node *) planstate,
 
1287
                                                                                        ancestors,
 
1288
                                                                                        es->rtable);
 
1289
        useprefix = list_length(es->rtable) > 1;
 
1290
 
 
1291
        /* Deparse each result column (we now include resjunk ones) */
 
1292
        foreach(lc, plan->targetlist)
 
1293
        {
 
1294
                TargetEntry *tle = (TargetEntry *) lfirst(lc);
 
1295
 
 
1296
                result = lappend(result,
 
1297
                                                 deparse_expression((Node *) tle->expr, context,
 
1298
                                                                                        useprefix, false));
 
1299
        }
 
1300
 
 
1301
        /* Print results */
 
1302
        ExplainPropertyList("Output", result, es);
 
1303
}
 
1304
 
 
1305
/*
 
1306
 * Show a generic expression
 
1307
 */
 
1308
static void
 
1309
show_expression(Node *node, const char *qlabel,
 
1310
                                PlanState *planstate, List *ancestors,
 
1311
                                bool useprefix, ExplainState *es)
 
1312
{
 
1313
        List       *context;
 
1314
        char       *exprstr;
 
1315
 
 
1316
        /* Set up deparsing context */
 
1317
        context = deparse_context_for_planstate((Node *) planstate,
 
1318
                                                                                        ancestors,
 
1319
                                                                                        es->rtable);
 
1320
 
 
1321
        /* Deparse the expression */
 
1322
        exprstr = deparse_expression(node, context, useprefix, false);
 
1323
 
 
1324
        /* And add to es->str */
 
1325
        ExplainPropertyText(qlabel, exprstr, es);
 
1326
}
 
1327
 
 
1328
/*
 
1329
 * Show a qualifier expression (which is a List with implicit AND semantics)
 
1330
 */
 
1331
static void
 
1332
show_qual(List *qual, const char *qlabel,
 
1333
                  PlanState *planstate, List *ancestors,
 
1334
                  bool useprefix, ExplainState *es)
 
1335
{
 
1336
        Node       *node;
 
1337
 
 
1338
        /* No work if empty qual */
 
1339
        if (qual == NIL)
 
1340
                return;
 
1341
 
 
1342
        /* Convert AND list to explicit AND */
 
1343
        node = (Node *) make_ands_explicit(qual);
 
1344
 
 
1345
        /* And show it */
 
1346
        show_expression(node, qlabel, planstate, ancestors, useprefix, es);
 
1347
}
 
1348
 
 
1349
/*
 
1350
 * Show a qualifier expression for a scan plan node
 
1351
 */
 
1352
static void
 
1353
show_scan_qual(List *qual, const char *qlabel,
 
1354
                           PlanState *planstate, List *ancestors,
 
1355
                           ExplainState *es)
 
1356
{
 
1357
        bool            useprefix;
 
1358
 
 
1359
        useprefix = (IsA(planstate->plan, SubqueryScan) ||es->verbose);
 
1360
        show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
 
1361
}
 
1362
 
 
1363
/*
 
1364
 * Show a qualifier expression for an upper-level plan node
 
1365
 */
 
1366
static void
 
1367
show_upper_qual(List *qual, const char *qlabel,
 
1368
                                PlanState *planstate, List *ancestors,
 
1369
                                ExplainState *es)
 
1370
{
 
1371
        bool            useprefix;
 
1372
 
 
1373
        useprefix = (list_length(es->rtable) > 1 || es->verbose);
 
1374
        show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
 
1375
}
 
1376
 
 
1377
/*
 
1378
 * Show the sort keys for a Sort node.
 
1379
 */
 
1380
static void
 
1381
show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
 
1382
{
 
1383
        Sort       *plan = (Sort *) sortstate->ss.ps.plan;
 
1384
 
 
1385
        show_sort_keys_common((PlanState *) sortstate,
 
1386
                                                  plan->numCols, plan->sortColIdx,
 
1387
                                                  ancestors, es);
 
1388
}
 
1389
 
 
1390
/*
 
1391
 * Likewise, for a MergeAppend node.
 
1392
 */
 
1393
static void
 
1394
show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
 
1395
                                           ExplainState *es)
 
1396
{
 
1397
        MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
 
1398
 
 
1399
        show_sort_keys_common((PlanState *) mstate,
 
1400
                                                  plan->numCols, plan->sortColIdx,
 
1401
                                                  ancestors, es);
 
1402
}
 
1403
 
 
1404
static void
 
1405
show_sort_keys_common(PlanState *planstate, int nkeys, AttrNumber *keycols,
 
1406
                                          List *ancestors, ExplainState *es)
 
1407
{
 
1408
        Plan       *plan = planstate->plan;
 
1409
        List       *context;
 
1410
        List       *result = NIL;
 
1411
        bool            useprefix;
 
1412
        int                     keyno;
 
1413
        char       *exprstr;
 
1414
 
 
1415
        if (nkeys <= 0)
 
1416
                return;
 
1417
 
 
1418
        /* Set up deparsing context */
 
1419
        context = deparse_context_for_planstate((Node *) planstate,
 
1420
                                                                                        ancestors,
 
1421
                                                                                        es->rtable);
 
1422
        useprefix = (list_length(es->rtable) > 1 || es->verbose);
 
1423
 
 
1424
        for (keyno = 0; keyno < nkeys; keyno++)
 
1425
        {
 
1426
                /* find key expression in tlist */
 
1427
                AttrNumber      keyresno = keycols[keyno];
 
1428
                TargetEntry *target = get_tle_by_resno(plan->targetlist,
 
1429
                                                                                           keyresno);
 
1430
 
 
1431
                if (!target)
 
1432
                        elog(ERROR, "no tlist entry for key %d", keyresno);
 
1433
                /* Deparse the expression, showing any top-level cast */
 
1434
                exprstr = deparse_expression((Node *) target->expr, context,
 
1435
                                                                         useprefix, true);
 
1436
                result = lappend(result, exprstr);
 
1437
        }
 
1438
 
 
1439
        ExplainPropertyList("Sort Key", result, es);
 
1440
}
 
1441
 
 
1442
/*
 
1443
 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
 
1444
 */
 
1445
static void
 
1446
show_sort_info(SortState *sortstate, ExplainState *es)
 
1447
{
 
1448
        Assert(IsA(sortstate, SortState));
 
1449
        if (es->analyze && sortstate->sort_Done &&
 
1450
                sortstate->tuplesortstate != NULL)
 
1451
        {
 
1452
                Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
 
1453
                const char *sortMethod;
 
1454
                const char *spaceType;
 
1455
                long            spaceUsed;
 
1456
 
 
1457
                tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
 
1458
 
 
1459
                if (es->format == EXPLAIN_FORMAT_TEXT)
 
1460
                {
 
1461
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1462
                        appendStringInfo(es->str, "Sort Method: %s  %s: %ldkB\n",
 
1463
                                                         sortMethod, spaceType, spaceUsed);
 
1464
                }
 
1465
                else
 
1466
                {
 
1467
                        ExplainPropertyText("Sort Method", sortMethod, es);
 
1468
                        ExplainPropertyLong("Sort Space Used", spaceUsed, es);
 
1469
                        ExplainPropertyText("Sort Space Type", spaceType, es);
 
1470
                }
 
1471
        }
 
1472
}
 
1473
 
 
1474
/*
 
1475
 * Show information on hash buckets/batches.
 
1476
 */
 
1477
static void
 
1478
show_hash_info(HashState *hashstate, ExplainState *es)
 
1479
{
 
1480
        HashJoinTable hashtable;
 
1481
 
 
1482
        Assert(IsA(hashstate, HashState));
 
1483
        hashtable = hashstate->hashtable;
 
1484
 
 
1485
        if (hashtable)
 
1486
        {
 
1487
                long            spacePeakKb = (hashtable->spacePeak + 1023) / 1024;
 
1488
 
 
1489
                if (es->format != EXPLAIN_FORMAT_TEXT)
 
1490
                {
 
1491
                        ExplainPropertyLong("Hash Buckets", hashtable->nbuckets, es);
 
1492
                        ExplainPropertyLong("Hash Batches", hashtable->nbatch, es);
 
1493
                        ExplainPropertyLong("Original Hash Batches",
 
1494
                                                                hashtable->nbatch_original, es);
 
1495
                        ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es);
 
1496
                }
 
1497
                else if (hashtable->nbatch_original != hashtable->nbatch)
 
1498
                {
 
1499
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1500
                        appendStringInfo(es->str,
 
1501
                        "Buckets: %d  Batches: %d (originally %d)  Memory Usage: %ldkB\n",
 
1502
                                                         hashtable->nbuckets, hashtable->nbatch,
 
1503
                                                         hashtable->nbatch_original, spacePeakKb);
 
1504
                }
 
1505
                else
 
1506
                {
 
1507
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1508
                        appendStringInfo(es->str,
 
1509
                                                   "Buckets: %d  Batches: %d  Memory Usage: %ldkB\n",
 
1510
                                                         hashtable->nbuckets, hashtable->nbatch,
 
1511
                                                         spacePeakKb);
 
1512
                }
 
1513
        }
 
1514
}
 
1515
 
 
1516
/*
 
1517
 * Show extra information for a ForeignScan node.
 
1518
 */
 
1519
static void
 
1520
show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
 
1521
{
 
1522
        FdwRoutine *fdwroutine = fsstate->fdwroutine;
 
1523
 
 
1524
        /* Let the FDW emit whatever fields it wants */
 
1525
        fdwroutine->ExplainForeignScan(fsstate, es);
 
1526
}
 
1527
 
 
1528
/*
 
1529
 * Fetch the name of an index in an EXPLAIN
 
1530
 *
 
1531
 * We allow plugins to get control here so that plans involving hypothetical
 
1532
 * indexes can be explained.
 
1533
 */
 
1534
static const char *
 
1535
explain_get_index_name(Oid indexId)
 
1536
{
 
1537
        const char *result;
 
1538
 
 
1539
        if (explain_get_index_name_hook)
 
1540
                result = (*explain_get_index_name_hook) (indexId);
 
1541
        else
 
1542
                result = NULL;
 
1543
        if (result == NULL)
 
1544
        {
 
1545
                /* default behavior: look in the catalogs and quote it */
 
1546
                result = get_rel_name(indexId);
 
1547
                if (result == NULL)
 
1548
                        elog(ERROR, "cache lookup failed for index %u", indexId);
 
1549
                result = quote_identifier(result);
 
1550
        }
 
1551
        return result;
 
1552
}
 
1553
 
 
1554
/*
 
1555
 * Show the target of a Scan node
 
1556
 */
 
1557
static void
 
1558
ExplainScanTarget(Scan *plan, ExplainState *es)
 
1559
{
 
1560
        ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
 
1561
}
 
1562
 
 
1563
/*
 
1564
 * Show the target of a ModifyTable node
 
1565
 */
 
1566
static void
 
1567
ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
 
1568
{
 
1569
        Index           rti;
 
1570
 
 
1571
        /*
 
1572
         * We show the name of the first target relation.  In multi-target-table
 
1573
         * cases this should always be the parent of the inheritance tree.
 
1574
         */
 
1575
        Assert(plan->resultRelations != NIL);
 
1576
        rti = linitial_int(plan->resultRelations);
 
1577
 
 
1578
        ExplainTargetRel((Plan *) plan, rti, es);
 
1579
}
 
1580
 
 
1581
/*
 
1582
 * Show the target relation of a scan or modify node
 
1583
 */
 
1584
static void
 
1585
ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 
1586
{
 
1587
        char       *objectname = NULL;
 
1588
        char       *namespace = NULL;
 
1589
        const char *objecttag = NULL;
 
1590
        RangeTblEntry *rte;
 
1591
 
 
1592
        rte = rt_fetch(rti, es->rtable);
 
1593
 
 
1594
        switch (nodeTag(plan))
 
1595
        {
 
1596
                case T_SeqScan:
 
1597
                case T_IndexScan:
 
1598
                case T_BitmapHeapScan:
 
1599
                case T_TidScan:
 
1600
                case T_ForeignScan:
 
1601
                case T_ModifyTable:
 
1602
                        /* Assert it's on a real relation */
 
1603
                        Assert(rte->rtekind == RTE_RELATION);
 
1604
                        objectname = get_rel_name(rte->relid);
 
1605
                        if (es->verbose)
 
1606
                                namespace = get_namespace_name(get_rel_namespace(rte->relid));
 
1607
                        objecttag = "Relation Name";
 
1608
                        break;
 
1609
                case T_FunctionScan:
 
1610
                        {
 
1611
                                Node       *funcexpr;
 
1612
 
 
1613
                                /* Assert it's on a RangeFunction */
 
1614
                                Assert(rte->rtekind == RTE_FUNCTION);
 
1615
 
 
1616
                                /*
 
1617
                                 * If the expression is still a function call, we can get the
 
1618
                                 * real name of the function.  Otherwise, punt (this can
 
1619
                                 * happen if the optimizer simplified away the function call,
 
1620
                                 * for example).
 
1621
                                 */
 
1622
                                funcexpr = ((FunctionScan *) plan)->funcexpr;
 
1623
                                if (funcexpr && IsA(funcexpr, FuncExpr))
 
1624
                                {
 
1625
                                        Oid                     funcid = ((FuncExpr *) funcexpr)->funcid;
 
1626
 
 
1627
                                        objectname = get_func_name(funcid);
 
1628
                                        if (es->verbose)
 
1629
                                                namespace =
 
1630
                                                        get_namespace_name(get_func_namespace(funcid));
 
1631
                                }
 
1632
                                objecttag = "Function Name";
 
1633
                        }
 
1634
                        break;
 
1635
                case T_ValuesScan:
 
1636
                        Assert(rte->rtekind == RTE_VALUES);
 
1637
                        break;
 
1638
                case T_CteScan:
 
1639
                        /* Assert it's on a non-self-reference CTE */
 
1640
                        Assert(rte->rtekind == RTE_CTE);
 
1641
                        Assert(!rte->self_reference);
 
1642
                        objectname = rte->ctename;
 
1643
                        objecttag = "CTE Name";
 
1644
                        break;
 
1645
                case T_WorkTableScan:
 
1646
                        /* Assert it's on a self-reference CTE */
 
1647
                        Assert(rte->rtekind == RTE_CTE);
 
1648
                        Assert(rte->self_reference);
 
1649
                        objectname = rte->ctename;
 
1650
                        objecttag = "CTE Name";
 
1651
                        break;
 
1652
                default:
 
1653
                        break;
 
1654
        }
 
1655
 
 
1656
        if (es->format == EXPLAIN_FORMAT_TEXT)
 
1657
        {
 
1658
                appendStringInfoString(es->str, " on");
 
1659
                if (namespace != NULL)
 
1660
                        appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
 
1661
                                                         quote_identifier(objectname));
 
1662
                else if (objectname != NULL)
 
1663
                        appendStringInfo(es->str, " %s", quote_identifier(objectname));
 
1664
                if (objectname == NULL ||
 
1665
                        strcmp(rte->eref->aliasname, objectname) != 0)
 
1666
                        appendStringInfo(es->str, " %s",
 
1667
                                                         quote_identifier(rte->eref->aliasname));
 
1668
        }
 
1669
        else
 
1670
        {
 
1671
                if (objecttag != NULL && objectname != NULL)
 
1672
                        ExplainPropertyText(objecttag, objectname, es);
 
1673
                if (namespace != NULL)
 
1674
                        ExplainPropertyText("Schema", namespace, es);
 
1675
                ExplainPropertyText("Alias", rte->eref->aliasname, es);
 
1676
        }
 
1677
}
 
1678
 
 
1679
/*
 
1680
 * Explain the constituent plans of a ModifyTable, Append, MergeAppend,
 
1681
 * BitmapAnd, or BitmapOr node.
 
1682
 *
 
1683
 * The ancestors list should already contain the immediate parent of these
 
1684
 * plans.
 
1685
 *
 
1686
 * Note: we don't actually need to examine the Plan list members, but
 
1687
 * we need the list in order to determine the length of the PlanState array.
 
1688
 */
 
1689
static void
 
1690
ExplainMemberNodes(List *plans, PlanState **planstates,
 
1691
                                   List *ancestors, ExplainState *es)
 
1692
{
 
1693
        int                     nplans = list_length(plans);
 
1694
        int                     j;
 
1695
 
 
1696
        for (j = 0; j < nplans; j++)
 
1697
                ExplainNode(planstates[j], ancestors,
 
1698
                                        "Member", NULL, es);
 
1699
}
 
1700
 
 
1701
/*
 
1702
 * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
 
1703
 *
 
1704
 * The ancestors list should already contain the immediate parent of these
 
1705
 * SubPlanStates.
 
1706
 */
 
1707
static void
 
1708
ExplainSubPlans(List *plans, List *ancestors,
 
1709
                                const char *relationship, ExplainState *es)
 
1710
{
 
1711
        ListCell   *lst;
 
1712
 
 
1713
        foreach(lst, plans)
 
1714
        {
 
1715
                SubPlanState *sps = (SubPlanState *) lfirst(lst);
 
1716
                SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
1717
 
 
1718
                ExplainNode(sps->planstate, ancestors,
 
1719
                                        relationship, sp->plan_name, es);
 
1720
        }
 
1721
}
 
1722
 
 
1723
/*
 
1724
 * Explain a property, such as sort keys or targets, that takes the form of
 
1725
 * a list of unlabeled items.  "data" is a list of C strings.
 
1726
 */
 
1727
void
 
1728
ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
 
1729
{
 
1730
        ListCell   *lc;
 
1731
        bool            first = true;
 
1732
 
 
1733
        switch (es->format)
 
1734
        {
 
1735
                case EXPLAIN_FORMAT_TEXT:
 
1736
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1737
                        appendStringInfo(es->str, "%s: ", qlabel);
 
1738
                        foreach(lc, data)
 
1739
                        {
 
1740
                                if (!first)
 
1741
                                        appendStringInfoString(es->str, ", ");
 
1742
                                appendStringInfoString(es->str, (const char *) lfirst(lc));
 
1743
                                first = false;
 
1744
                        }
 
1745
                        appendStringInfoChar(es->str, '\n');
 
1746
                        break;
 
1747
 
 
1748
                case EXPLAIN_FORMAT_XML:
 
1749
                        ExplainXMLTag(qlabel, X_OPENING, es);
 
1750
                        foreach(lc, data)
 
1751
                        {
 
1752
                                char       *str;
 
1753
 
 
1754
                                appendStringInfoSpaces(es->str, es->indent * 2 + 2);
 
1755
                                appendStringInfoString(es->str, "<Item>");
 
1756
                                str = escape_xml((const char *) lfirst(lc));
 
1757
                                appendStringInfoString(es->str, str);
 
1758
                                pfree(str);
 
1759
                                appendStringInfoString(es->str, "</Item>\n");
 
1760
                        }
 
1761
                        ExplainXMLTag(qlabel, X_CLOSING, es);
 
1762
                        break;
 
1763
 
 
1764
                case EXPLAIN_FORMAT_JSON:
 
1765
                        ExplainJSONLineEnding(es);
 
1766
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1767
                        escape_json(es->str, qlabel);
 
1768
                        appendStringInfoString(es->str, ": [");
 
1769
                        foreach(lc, data)
 
1770
                        {
 
1771
                                if (!first)
 
1772
                                        appendStringInfoString(es->str, ", ");
 
1773
                                escape_json(es->str, (const char *) lfirst(lc));
 
1774
                                first = false;
 
1775
                        }
 
1776
                        appendStringInfoChar(es->str, ']');
 
1777
                        break;
 
1778
 
 
1779
                case EXPLAIN_FORMAT_YAML:
 
1780
                        ExplainYAMLLineStarting(es);
 
1781
                        appendStringInfo(es->str, "%s: ", qlabel);
 
1782
                        foreach(lc, data)
 
1783
                        {
 
1784
                                appendStringInfoChar(es->str, '\n');
 
1785
                                appendStringInfoSpaces(es->str, es->indent * 2 + 2);
 
1786
                                appendStringInfoString(es->str, "- ");
 
1787
                                escape_yaml(es->str, (const char *) lfirst(lc));
 
1788
                        }
 
1789
                        break;
 
1790
        }
 
1791
}
 
1792
 
 
1793
/*
 
1794
 * Explain a simple property.
 
1795
 *
 
1796
 * If "numeric" is true, the value is a number (or other value that
 
1797
 * doesn't need quoting in JSON).
 
1798
 *
 
1799
 * This usually should not be invoked directly, but via one of the datatype
 
1800
 * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
 
1801
 */
 
1802
static void
 
1803
ExplainProperty(const char *qlabel, const char *value, bool numeric,
 
1804
                                ExplainState *es)
 
1805
{
 
1806
        switch (es->format)
 
1807
        {
 
1808
                case EXPLAIN_FORMAT_TEXT:
 
1809
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1810
                        appendStringInfo(es->str, "%s: %s\n", qlabel, value);
 
1811
                        break;
 
1812
 
 
1813
                case EXPLAIN_FORMAT_XML:
 
1814
                        {
 
1815
                                char       *str;
 
1816
 
 
1817
                                appendStringInfoSpaces(es->str, es->indent * 2);
 
1818
                                ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
 
1819
                                str = escape_xml(value);
 
1820
                                appendStringInfoString(es->str, str);
 
1821
                                pfree(str);
 
1822
                                ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
 
1823
                                appendStringInfoChar(es->str, '\n');
 
1824
                        }
 
1825
                        break;
 
1826
 
 
1827
                case EXPLAIN_FORMAT_JSON:
 
1828
                        ExplainJSONLineEnding(es);
 
1829
                        appendStringInfoSpaces(es->str, es->indent * 2);
 
1830
                        escape_json(es->str, qlabel);
 
1831
                        appendStringInfoString(es->str, ": ");
 
1832
                        if (numeric)
 
1833
                                appendStringInfoString(es->str, value);
 
1834
                        else
 
1835
                                escape_json(es->str, value);
 
1836
                        break;
 
1837
 
 
1838
                case EXPLAIN_FORMAT_YAML:
 
1839
                        ExplainYAMLLineStarting(es);
 
1840
                        appendStringInfo(es->str, "%s: ", qlabel);
 
1841
                        if (numeric)
 
1842
                                appendStringInfoString(es->str, value);
 
1843
                        else
 
1844
                                escape_yaml(es->str, value);
 
1845
                        break;
 
1846
        }
 
1847
}
 
1848
 
 
1849
/*
 
1850
 * Explain a string-valued property.
 
1851
 */
 
1852
void
 
1853
ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
 
1854
{
 
1855
        ExplainProperty(qlabel, value, false, es);
 
1856
}
 
1857
 
 
1858
/*
 
1859
 * Explain an integer-valued property.
 
1860
 */
 
1861
void
 
1862
ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
 
1863
{
 
1864
        char            buf[32];
 
1865
 
 
1866
        snprintf(buf, sizeof(buf), "%d", value);
 
1867
        ExplainProperty(qlabel, buf, true, es);
 
1868
}
 
1869
 
 
1870
/*
 
1871
 * Explain a long-integer-valued property.
 
1872
 */
 
1873
void
 
1874
ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
 
1875
{
 
1876
        char            buf[32];
 
1877
 
 
1878
        snprintf(buf, sizeof(buf), "%ld", value);
 
1879
        ExplainProperty(qlabel, buf, true, es);
 
1880
}
 
1881
 
 
1882
/*
 
1883
 * Explain a float-valued property, using the specified number of
 
1884
 * fractional digits.
 
1885
 */
 
1886
void
 
1887
ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
 
1888
                                         ExplainState *es)
 
1889
{
 
1890
        char            buf[256];
 
1891
 
 
1892
        snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
 
1893
        ExplainProperty(qlabel, buf, true, es);
 
1894
}
 
1895
 
 
1896
/*
 
1897
 * Open a group of related objects.
 
1898
 *
 
1899
 * objtype is the type of the group object, labelname is its label within
 
1900
 * a containing object (if any).
 
1901
 *
 
1902
 * If labeled is true, the group members will be labeled properties,
 
1903
 * while if it's false, they'll be unlabeled objects.
 
1904
 */
 
1905
static void
 
1906
ExplainOpenGroup(const char *objtype, const char *labelname,
 
1907
                                 bool labeled, ExplainState *es)
 
1908
{
 
1909
        switch (es->format)
 
1910
        {
 
1911
                case EXPLAIN_FORMAT_TEXT:
 
1912
                        /* nothing to do */
 
1913
                        break;
 
1914
 
 
1915
                case EXPLAIN_FORMAT_XML:
 
1916
                        ExplainXMLTag(objtype, X_OPENING, es);
 
1917
                        es->indent++;
 
1918
                        break;
 
1919
 
 
1920
                case EXPLAIN_FORMAT_JSON:
 
1921
                        ExplainJSONLineEnding(es);
 
1922
                        appendStringInfoSpaces(es->str, 2 * es->indent);
 
1923
                        if (labelname)
 
1924
                        {
 
1925
                                escape_json(es->str, labelname);
 
1926
                                appendStringInfoString(es->str, ": ");
 
1927
                        }
 
1928
                        appendStringInfoChar(es->str, labeled ? '{' : '[');
 
1929
 
 
1930
                        /*
 
1931
                         * In JSON format, the grouping_stack is an integer list.  0 means
 
1932
                         * we've emitted nothing at this grouping level, 1 means we've
 
1933
                         * emitted something (and so the next item needs a comma). See
 
1934
                         * ExplainJSONLineEnding().
 
1935
                         */
 
1936
                        es->grouping_stack = lcons_int(0, es->grouping_stack);
 
1937
                        es->indent++;
 
1938
                        break;
 
1939
 
 
1940
                case EXPLAIN_FORMAT_YAML:
 
1941
 
 
1942
                        /*
 
1943
                         * In YAML format, the grouping stack is an integer list.  0 means
 
1944
                         * we've emitted nothing at this grouping level AND this grouping
 
1945
                         * level is unlabelled and must be marked with "- ".  See
 
1946
                         * ExplainYAMLLineStarting().
 
1947
                         */
 
1948
                        ExplainYAMLLineStarting(es);
 
1949
                        if (labelname)
 
1950
                        {
 
1951
                                appendStringInfo(es->str, "%s: ", labelname);
 
1952
                                es->grouping_stack = lcons_int(1, es->grouping_stack);
 
1953
                        }
 
1954
                        else
 
1955
                        {
 
1956
                                appendStringInfoString(es->str, "- ");
 
1957
                                es->grouping_stack = lcons_int(0, es->grouping_stack);
 
1958
                        }
 
1959
                        es->indent++;
 
1960
                        break;
 
1961
        }
 
1962
}
 
1963
 
 
1964
/*
 
1965
 * Close a group of related objects.
 
1966
 * Parameters must match the corresponding ExplainOpenGroup call.
 
1967
 */
 
1968
static void
 
1969
ExplainCloseGroup(const char *objtype, const char *labelname,
 
1970
                                  bool labeled, ExplainState *es)
 
1971
{
 
1972
        switch (es->format)
 
1973
        {
 
1974
                case EXPLAIN_FORMAT_TEXT:
 
1975
                        /* nothing to do */
 
1976
                        break;
 
1977
 
 
1978
                case EXPLAIN_FORMAT_XML:
 
1979
                        es->indent--;
 
1980
                        ExplainXMLTag(objtype, X_CLOSING, es);
 
1981
                        break;
 
1982
 
 
1983
                case EXPLAIN_FORMAT_JSON:
 
1984
                        es->indent--;
 
1985
                        appendStringInfoChar(es->str, '\n');
 
1986
                        appendStringInfoSpaces(es->str, 2 * es->indent);
 
1987
                        appendStringInfoChar(es->str, labeled ? '}' : ']');
 
1988
                        es->grouping_stack = list_delete_first(es->grouping_stack);
 
1989
                        break;
 
1990
 
 
1991
                case EXPLAIN_FORMAT_YAML:
 
1992
                        es->indent--;
 
1993
                        es->grouping_stack = list_delete_first(es->grouping_stack);
 
1994
                        break;
 
1995
        }
 
1996
}
 
1997
 
 
1998
/*
 
1999
 * Emit a "dummy" group that never has any members.
 
2000
 *
 
2001
 * objtype is the type of the group object, labelname is its label within
 
2002
 * a containing object (if any).
 
2003
 */
 
2004
static void
 
2005
ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
 
2006
{
 
2007
        switch (es->format)
 
2008
        {
 
2009
                case EXPLAIN_FORMAT_TEXT:
 
2010
                        /* nothing to do */
 
2011
                        break;
 
2012
 
 
2013
                case EXPLAIN_FORMAT_XML:
 
2014
                        ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
 
2015
                        break;
 
2016
 
 
2017
                case EXPLAIN_FORMAT_JSON:
 
2018
                        ExplainJSONLineEnding(es);
 
2019
                        appendStringInfoSpaces(es->str, 2 * es->indent);
 
2020
                        if (labelname)
 
2021
                        {
 
2022
                                escape_json(es->str, labelname);
 
2023
                                appendStringInfoString(es->str, ": ");
 
2024
                        }
 
2025
                        escape_json(es->str, objtype);
 
2026
                        break;
 
2027
 
 
2028
                case EXPLAIN_FORMAT_YAML:
 
2029
                        ExplainYAMLLineStarting(es);
 
2030
                        if (labelname)
 
2031
                        {
 
2032
                                escape_yaml(es->str, labelname);
 
2033
                                appendStringInfoString(es->str, ": ");
 
2034
                        }
 
2035
                        else
 
2036
                        {
 
2037
                                appendStringInfoString(es->str, "- ");
 
2038
                        }
 
2039
                        escape_yaml(es->str, objtype);
 
2040
                        break;
 
2041
        }
 
2042
}
 
2043
 
 
2044
/*
 
2045
 * Emit the start-of-output boilerplate.
 
2046
 *
 
2047
 * This is just enough different from processing a subgroup that we need
 
2048
 * a separate pair of subroutines.
 
2049
 */
 
2050
void
 
2051
ExplainBeginOutput(ExplainState *es)
 
2052
{
 
2053
        switch (es->format)
 
2054
        {
 
2055
                case EXPLAIN_FORMAT_TEXT:
 
2056
                        /* nothing to do */
 
2057
                        break;
 
2058
 
 
2059
                case EXPLAIN_FORMAT_XML:
 
2060
                        appendStringInfoString(es->str,
 
2061
                         "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
 
2062
                        es->indent++;
 
2063
                        break;
 
2064
 
 
2065
                case EXPLAIN_FORMAT_JSON:
 
2066
                        /* top-level structure is an array of plans */
 
2067
                        appendStringInfoChar(es->str, '[');
 
2068
                        es->grouping_stack = lcons_int(0, es->grouping_stack);
 
2069
                        es->indent++;
 
2070
                        break;
 
2071
 
 
2072
                case EXPLAIN_FORMAT_YAML:
 
2073
                        es->grouping_stack = lcons_int(0, es->grouping_stack);
 
2074
                        break;
 
2075
        }
 
2076
}
 
2077
 
 
2078
/*
 
2079
 * Emit the end-of-output boilerplate.
 
2080
 */
 
2081
void
 
2082
ExplainEndOutput(ExplainState *es)
 
2083
{
 
2084
        switch (es->format)
 
2085
        {
 
2086
                case EXPLAIN_FORMAT_TEXT:
 
2087
                        /* nothing to do */
 
2088
                        break;
 
2089
 
 
2090
                case EXPLAIN_FORMAT_XML:
 
2091
                        es->indent--;
 
2092
                        appendStringInfoString(es->str, "</explain>");
 
2093
                        break;
 
2094
 
 
2095
                case EXPLAIN_FORMAT_JSON:
 
2096
                        es->indent--;
 
2097
                        appendStringInfoString(es->str, "\n]");
 
2098
                        es->grouping_stack = list_delete_first(es->grouping_stack);
 
2099
                        break;
 
2100
 
 
2101
                case EXPLAIN_FORMAT_YAML:
 
2102
                        es->grouping_stack = list_delete_first(es->grouping_stack);
 
2103
                        break;
 
2104
        }
 
2105
}
 
2106
 
 
2107
/*
 
2108
 * Put an appropriate separator between multiple plans
 
2109
 */
 
2110
void
 
2111
ExplainSeparatePlans(ExplainState *es)
 
2112
{
 
2113
        switch (es->format)
 
2114
        {
 
2115
                case EXPLAIN_FORMAT_TEXT:
 
2116
                        /* add a blank line */
 
2117
                        appendStringInfoChar(es->str, '\n');
 
2118
                        break;
 
2119
 
 
2120
                case EXPLAIN_FORMAT_XML:
 
2121
                case EXPLAIN_FORMAT_JSON:
 
2122
                case EXPLAIN_FORMAT_YAML:
 
2123
                        /* nothing to do */
 
2124
                        break;
 
2125
        }
 
2126
}
 
2127
 
 
2128
/*
 
2129
 * Emit opening or closing XML tag.
 
2130
 *
 
2131
 * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
 
2132
 * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
 
2133
 * add.
 
2134
 *
 
2135
 * XML tag names can't contain white space, so we replace any spaces in
 
2136
 * "tagname" with dashes.
 
2137
 */
 
2138
static void
 
2139
ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
 
2140
{
 
2141
        const char *s;
 
2142
 
 
2143
        if ((flags & X_NOWHITESPACE) == 0)
 
2144
                appendStringInfoSpaces(es->str, 2 * es->indent);
 
2145
        appendStringInfoCharMacro(es->str, '<');
 
2146
        if ((flags & X_CLOSING) != 0)
 
2147
                appendStringInfoCharMacro(es->str, '/');
 
2148
        for (s = tagname; *s; s++)
 
2149
                appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
 
2150
        if ((flags & X_CLOSE_IMMEDIATE) != 0)
 
2151
                appendStringInfoString(es->str, " /");
 
2152
        appendStringInfoCharMacro(es->str, '>');
 
2153
        if ((flags & X_NOWHITESPACE) == 0)
 
2154
                appendStringInfoCharMacro(es->str, '\n');
 
2155
}
 
2156
 
 
2157
/*
 
2158
 * Emit a JSON line ending.
 
2159
 *
 
2160
 * JSON requires a comma after each property but the last.      To facilitate this,
 
2161
 * in JSON format, the text emitted for each property begins just prior to the
 
2162
 * preceding line-break (and comma, if applicable).
 
2163
 */
 
2164
static void
 
2165
ExplainJSONLineEnding(ExplainState *es)
 
2166
{
 
2167
        Assert(es->format == EXPLAIN_FORMAT_JSON);
 
2168
        if (linitial_int(es->grouping_stack) != 0)
 
2169
                appendStringInfoChar(es->str, ',');
 
2170
        else
 
2171
                linitial_int(es->grouping_stack) = 1;
 
2172
        appendStringInfoChar(es->str, '\n');
 
2173
}
 
2174
 
 
2175
/*
 
2176
 * Indent a YAML line.
 
2177
 *
 
2178
 * YAML lines are ordinarily indented by two spaces per indentation level.
 
2179
 * The text emitted for each property begins just prior to the preceding
 
2180
 * line-break, except for the first property in an unlabelled group, for which
 
2181
 * it begins immediately after the "- " that introduces the group.      The first
 
2182
 * property of the group appears on the same line as the opening "- ".
 
2183
 */
 
2184
static void
 
2185
ExplainYAMLLineStarting(ExplainState *es)
 
2186
{
 
2187
        Assert(es->format == EXPLAIN_FORMAT_YAML);
 
2188
        if (linitial_int(es->grouping_stack) == 0)
 
2189
        {
 
2190
                linitial_int(es->grouping_stack) = 1;
 
2191
        }
 
2192
        else
 
2193
        {
 
2194
                appendStringInfoChar(es->str, '\n');
 
2195
                appendStringInfoSpaces(es->str, es->indent * 2);
 
2196
        }
 
2197
}
 
2198
 
 
2199
/*
 
2200
 * Produce a JSON string literal, properly escaping characters in the text.
 
2201
 */
 
2202
static void
 
2203
escape_json(StringInfo buf, const char *str)
 
2204
{
 
2205
        const char *p;
 
2206
 
 
2207
        appendStringInfoCharMacro(buf, '\"');
 
2208
        for (p = str; *p; p++)
 
2209
        {
 
2210
                switch (*p)
 
2211
                {
 
2212
                        case '\b':
 
2213
                                appendStringInfoString(buf, "\\b");
 
2214
                                break;
 
2215
                        case '\f':
 
2216
                                appendStringInfoString(buf, "\\f");
 
2217
                                break;
 
2218
                        case '\n':
 
2219
                                appendStringInfoString(buf, "\\n");
 
2220
                                break;
 
2221
                        case '\r':
 
2222
                                appendStringInfoString(buf, "\\r");
 
2223
                                break;
 
2224
                        case '\t':
 
2225
                                appendStringInfoString(buf, "\\t");
 
2226
                                break;
 
2227
                        case '"':
 
2228
                                appendStringInfoString(buf, "\\\"");
 
2229
                                break;
 
2230
                        case '\\':
 
2231
                                appendStringInfoString(buf, "\\\\");
 
2232
                                break;
 
2233
                        default:
 
2234
                                if ((unsigned char) *p < ' ')
 
2235
                                        appendStringInfo(buf, "\\u%04x", (int) *p);
 
2236
                                else
 
2237
                                        appendStringInfoCharMacro(buf, *p);
 
2238
                                break;
 
2239
                }
 
2240
        }
 
2241
        appendStringInfoCharMacro(buf, '\"');
 
2242
}
 
2243
 
 
2244
/*
 
2245
 * YAML is a superset of JSON; unfortuantely, the YAML quoting rules are
 
2246
 * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
 
2247
 * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
 
2248
 * Empty strings, strings with leading or trailing whitespace, and strings
 
2249
 * containing a variety of special characters must certainly be quoted or the
 
2250
 * output is invalid; and other seemingly harmless strings like "0xa" or
 
2251
 * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
 
2252
 * constant rather than a string.
 
2253
 */
 
2254
static void
 
2255
escape_yaml(StringInfo buf, const char *str)
 
2256
{
 
2257
        escape_json(buf, str);
 
2258
}