Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

explain.c File Reference

#include "postgres.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/defrem.h"
#include "commands/prepare.h"
#include "executor/hashjoin.h"
#include "foreign/fdwapi.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/json.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
#include "utils/xml.h"
Include dependency graph for explain.c:

Go to the source code of this file.

Defines

#define X_OPENING   0
#define X_CLOSING   1
#define X_CLOSE_IMMEDIATE   2
#define X_NOWHITESPACE   4

Functions

static void ExplainOneQuery (Query *query, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params)
static void report_triggers (ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
static double elapsed_time (instr_time *starttime)
static void ExplainPreScanNode (PlanState *planstate, Bitmapset **rels_used)
static void ExplainPreScanMemberNodes (List *plans, PlanState **planstates, Bitmapset **rels_used)
static void ExplainPreScanSubPlans (List *plans, Bitmapset **rels_used)
static void ExplainNode (PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
static void show_plan_tlist (PlanState *planstate, List *ancestors, ExplainState *es)
static void show_expression (Node *node, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
static void show_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
static void show_scan_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
static void show_upper_qual (List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
static void show_sort_keys (SortState *sortstate, List *ancestors, ExplainState *es)
static void show_merge_append_keys (MergeAppendState *mstate, List *ancestors, ExplainState *es)
static void show_sort_keys_common (PlanState *planstate, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es)
static void show_sort_info (SortState *sortstate, ExplainState *es)
static void show_hash_info (HashState *hashstate, ExplainState *es)
static void show_instrumentation_count (const char *qlabel, int which, PlanState *planstate, ExplainState *es)
static void show_foreignscan_info (ForeignScanState *fsstate, ExplainState *es)
static const char * explain_get_index_name (Oid indexId)
static void ExplainIndexScanDetails (Oid indexid, ScanDirection indexorderdir, ExplainState *es)
static void ExplainScanTarget (Scan *plan, ExplainState *es)
static void ExplainModifyTarget (ModifyTable *plan, ExplainState *es)
static void ExplainTargetRel (Plan *plan, Index rti, ExplainState *es)
static void show_modifytable_info (ModifyTableState *mtstate, ExplainState *es)
static void ExplainMemberNodes (List *plans, PlanState **planstates, List *ancestors, ExplainState *es)
static void ExplainSubPlans (List *plans, List *ancestors, const char *relationship, ExplainState *es)
static void ExplainProperty (const char *qlabel, const char *value, bool numeric, ExplainState *es)
static void ExplainOpenGroup (const char *objtype, const char *labelname, bool labeled, ExplainState *es)
static void ExplainCloseGroup (const char *objtype, const char *labelname, bool labeled, ExplainState *es)
static void ExplainDummyGroup (const char *objtype, const char *labelname, ExplainState *es)
static void ExplainXMLTag (const char *tagname, int flags, ExplainState *es)
static void ExplainJSONLineEnding (ExplainState *es)
static void ExplainYAMLLineStarting (ExplainState *es)
static void escape_yaml (StringInfo buf, const char *str)
void ExplainQuery (ExplainStmt *stmt, const char *queryString, ParamListInfo params, DestReceiver *dest)
void ExplainInitState (ExplainState *es)
TupleDesc ExplainResultDesc (ExplainStmt *stmt)
void ExplainOneUtility (Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params)
void ExplainOnePlan (PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params)
void ExplainPrintPlan (ExplainState *es, QueryDesc *queryDesc)
void ExplainQueryText (ExplainState *es, QueryDesc *queryDesc)
void ExplainPropertyList (const char *qlabel, List *data, ExplainState *es)
void ExplainPropertyText (const char *qlabel, const char *value, ExplainState *es)
void ExplainPropertyInteger (const char *qlabel, int value, ExplainState *es)
void ExplainPropertyLong (const char *qlabel, long value, ExplainState *es)
void ExplainPropertyFloat (const char *qlabel, double value, int ndigits, ExplainState *es)
void ExplainBeginOutput (ExplainState *es)
void ExplainEndOutput (ExplainState *es)
void ExplainSeparatePlans (ExplainState *es)

Variables

ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL
explain_get_index_name_hook_type explain_get_index_name_hook = NULL

Define Documentation

#define X_CLOSE_IMMEDIATE   2

Definition at line 46 of file explain.c.

Referenced by ExplainDummyGroup(), and ExplainXMLTag().

#define X_CLOSING   1

Definition at line 45 of file explain.c.

Referenced by ExplainCloseGroup(), ExplainProperty(), ExplainPropertyList(), and ExplainXMLTag().

#define X_NOWHITESPACE   4

Definition at line 47 of file explain.c.

Referenced by ExplainProperty(), and ExplainXMLTag().

#define X_OPENING   0

Definition at line 44 of file explain.c.

Referenced by ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().


Function Documentation

static double elapsed_time ( instr_time starttime  )  [static]

Definition at line 643 of file explain.c.

References INSTR_TIME_GET_DOUBLE, INSTR_TIME_SET_CURRENT, and INSTR_TIME_SUBTRACT.

Referenced by ExplainOnePlan().

{
    instr_time  endtime;

    INSTR_TIME_SET_CURRENT(endtime);
    INSTR_TIME_SUBTRACT(endtime, *starttime);
    return INSTR_TIME_GET_DOUBLE(endtime);
}

static void escape_yaml ( StringInfo  buf,
const char *  str 
) [static]

Definition at line 2608 of file explain.c.

References escape_json().

Referenced by ExplainDummyGroup(), ExplainProperty(), and ExplainPropertyList().

{
    escape_json(buf, str);
}

static const char * explain_get_index_name ( Oid  indexId  )  [static]

Definition at line 1863 of file explain.c.

References elog, ERROR, explain_get_index_name_hook, get_rel_name(), NULL, and quote_identifier().

Referenced by ExplainIndexScanDetails(), and ExplainNode().

{
    const char *result;

    if (explain_get_index_name_hook)
        result = (*explain_get_index_name_hook) (indexId);
    else
        result = NULL;
    if (result == NULL)
    {
        /* default behavior: look in the catalogs and quote it */
        result = get_rel_name(indexId);
        if (result == NULL)
            elog(ERROR, "cache lookup failed for index %u", indexId);
        result = quote_identifier(result);
    }
    return result;
}

void ExplainBeginOutput ( ExplainState es  ) 

Definition at line 2449 of file explain.c.

References appendStringInfoChar(), appendStringInfoString(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, lcons_int(), and ExplainState::str.

Referenced by explain_ExecutorEnd(), and ExplainQuery().

{
    switch (es->format)
    {
        case EXPLAIN_FORMAT_TEXT:
            /* nothing to do */
            break;

        case EXPLAIN_FORMAT_XML:
            appendStringInfoString(es->str,
             "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
            es->indent++;
            break;

        case EXPLAIN_FORMAT_JSON:
            /* top-level structure is an array of plans */
            appendStringInfoChar(es->str, '[');
            es->grouping_stack = lcons_int(0, es->grouping_stack);
            es->indent++;
            break;

        case EXPLAIN_FORMAT_YAML:
            es->grouping_stack = lcons_int(0, es->grouping_stack);
            break;
    }
}

static void ExplainCloseGroup ( const char *  objtype,
const char *  labelname,
bool  labeled,
ExplainState es 
) [static]
static void ExplainDummyGroup ( const char *  objtype,
const char *  labelname,
ExplainState es 
) [static]
void ExplainEndOutput ( ExplainState es  ) 
static void ExplainIndexScanDetails ( Oid  indexid,
ScanDirection  indexorderdir,
ExplainState es 
) [static]

Definition at line 1886 of file explain.c.

References appendStringInfo(), appendStringInfoString(), BackwardScanDirection, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), ExplainPropertyText(), ExplainState::format, ForwardScanDirection, NoMovementScanDirection, ScanDirectionIsBackward, and ExplainState::str.

Referenced by ExplainNode().

{
    const char *indexname = explain_get_index_name(indexid);

    if (es->format == EXPLAIN_FORMAT_TEXT)
    {
        if (ScanDirectionIsBackward(indexorderdir))
            appendStringInfoString(es->str, " Backward");
        appendStringInfo(es->str, " using %s", indexname);
    }
    else
    {
        const char *scandir;

        switch (indexorderdir)
        {
            case BackwardScanDirection:
                scandir = "Backward";
                break;
            case NoMovementScanDirection:
                scandir = "NoMovement";
                break;
            case ForwardScanDirection:
                scandir = "Forward";
                break;
            default:
                scandir = "???";
                break;
        }
        ExplainPropertyText("Scan Direction", scandir, es);
        ExplainPropertyText("Index Name", indexname, es);
    }
}

void ExplainInitState ( ExplainState es  ) 

Definition at line 249 of file explain.c.

References ExplainState::costs, makeStringInfo(), and ExplainState::str.

Referenced by explain_ExecutorEnd(), and ExplainQuery().

{
    /* Set default options. */
    memset(es, 0, sizeof(ExplainState));
    es->costs = true;
    /* Prepare output buffer. */
    es->str = makeStringInfo();
}

static void ExplainJSONLineEnding ( ExplainState es  )  [static]
static void ExplainMemberNodes ( List plans,
PlanState **  planstates,
List ancestors,
ExplainState es 
) [static]

Definition at line 2088 of file explain.c.

References ExplainNode(), list_length(), and NULL.

Referenced by ExplainNode().

{
    int         nplans = list_length(plans);
    int         j;

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

static void ExplainModifyTarget ( ModifyTable plan,
ExplainState es 
) [static]

Definition at line 1934 of file explain.c.

References Assert, ExplainTargetRel(), linitial_int, NIL, and ModifyTable::resultRelations.

Referenced by ExplainNode().

{
    Index       rti;

    /*
     * We show the name of the first target relation.  In multi-target-table
     * cases this should always be the parent of the inheritance tree.
     */
    Assert(plan->resultRelations != NIL);
    rti = linitial_int(plan->resultRelations);

    ExplainTargetRel((Plan *) plan, rti, es);
}

static void ExplainNode ( PlanState planstate,
List ancestors,
const char *  relationship,
const char *  plan_name,
ExplainState es 
) [static]

Definition at line 799 of file explain.c.

References AGG_HASHED, AGG_PLAIN, AGG_SORTED, ExplainState::analyze, appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), BufferUsage::blk_read_time, BufferUsage::blk_write_time, ExplainState::buffers, Instrumentation::bufusage, CMD_DELETE, CMD_INSERT, CMD_UPDATE, ExplainState::costs, EXPLAIN_FORMAT_TEXT, explain_get_index_name(), ExplainCloseGroup(), ExplainIndexScanDetails(), ExplainMemberNodes(), ExplainModifyTarget(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyLong(), ExplainPropertyText(), ExplainScanTarget(), ExplainSubPlans(), ExplainState::format, ExplainState::indent, BitmapIndexScan::indexid, IndexOnlyScan::indexid, IndexScan::indexid, IndexOnlyScan::indexorderdir, IndexScan::indexorderdir, PlanState::initPlan, innerPlanState, INSTR_TIME_GET_MILLISEC, INSTR_TIME_IS_ZERO, InstrEndLoop(), PlanState::instrument, IsA, JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_SEMI, lcons(), list_delete_first(), list_length(), list_make1, BufferUsage::local_blks_dirtied, BufferUsage::local_blks_hit, BufferUsage::local_blks_read, BufferUsage::local_blks_written, make_orclause(), Instrumentation::need_timer, Instrumentation::nloops, nodeTag, Instrumentation::ntuples, NULL, outerPlanState, PlanState::plan, Plan::plan_rows, Plan::plan_width, SETOP_HASHED, SETOP_SORTED, SETOPCMD_EXCEPT, SETOPCMD_EXCEPT_ALL, SETOPCMD_INTERSECT, SETOPCMD_INTERSECT_ALL, BufferUsage::shared_blks_dirtied, BufferUsage::shared_blks_hit, BufferUsage::shared_blks_read, BufferUsage::shared_blks_written, show_expression(), show_foreignscan_info(), show_hash_info(), show_instrumentation_count(), show_merge_append_keys(), show_modifytable_info(), show_plan_tlist(), show_scan_qual(), show_sort_info(), show_sort_keys(), show_upper_qual(), Instrumentation::startup, Plan::startup_cost, ExplainState::str, PlanState::subPlan, T_Agg, T_Append, T_BitmapAnd, T_BitmapHeapScan, T_BitmapIndexScan, T_BitmapOr, T_CteScan, T_ForeignScan, T_FunctionScan, T_Group, T_Hash, T_HashJoin, T_IndexOnlyScan, T_IndexScan, T_Limit, T_LockRows, T_Material, T_MergeAppend, T_MergeJoin, T_ModifyTable, T_NestLoop, T_RecursiveUnion, T_Result, T_SeqScan, T_SetOp, T_Sort, T_SubqueryScan, T_TidScan, T_Unique, T_ValuesScan, T_WindowAgg, T_WorkTableScan, BufferUsage::temp_blks_read, BufferUsage::temp_blks_written, Instrumentation::total, Plan::total_cost, usage(), and ExplainState::verbose.

Referenced by ExplainMemberNodes(), ExplainPrintPlan(), and ExplainSubPlans().

{
    Plan       *plan = planstate->plan;
    const char *pname;          /* node type name for text output */
    const char *sname;          /* node type name for non-text output */
    const char *strategy = NULL;
    const char *operation = NULL;
    int         save_indent = es->indent;
    bool        haschildren;

    switch (nodeTag(plan))
    {
        case T_Result:
            pname = sname = "Result";
            break;
        case T_ModifyTable:
            sname = "ModifyTable";
            switch (((ModifyTable *) plan)->operation)
            {
                case CMD_INSERT:
                    pname = operation = "Insert";
                    break;
                case CMD_UPDATE:
                    pname = operation = "Update";
                    break;
                case CMD_DELETE:
                    pname = operation = "Delete";
                    break;
                default:
                    pname = "???";
                    break;
            }
            break;
        case T_Append:
            pname = sname = "Append";
            break;
        case T_MergeAppend:
            pname = sname = "Merge Append";
            break;
        case T_RecursiveUnion:
            pname = sname = "Recursive Union";
            break;
        case T_BitmapAnd:
            pname = sname = "BitmapAnd";
            break;
        case T_BitmapOr:
            pname = sname = "BitmapOr";
            break;
        case T_NestLoop:
            pname = sname = "Nested Loop";
            break;
        case T_MergeJoin:
            pname = "Merge";    /* "Join" gets added by jointype switch */
            sname = "Merge Join";
            break;
        case T_HashJoin:
            pname = "Hash";     /* "Join" gets added by jointype switch */
            sname = "Hash Join";
            break;
        case T_SeqScan:
            pname = sname = "Seq Scan";
            break;
        case T_IndexScan:
            pname = sname = "Index Scan";
            break;
        case T_IndexOnlyScan:
            pname = sname = "Index Only Scan";
            break;
        case T_BitmapIndexScan:
            pname = sname = "Bitmap Index Scan";
            break;
        case T_BitmapHeapScan:
            pname = sname = "Bitmap Heap Scan";
            break;
        case T_TidScan:
            pname = sname = "Tid Scan";
            break;
        case T_SubqueryScan:
            pname = sname = "Subquery Scan";
            break;
        case T_FunctionScan:
            pname = sname = "Function Scan";
            break;
        case T_ValuesScan:
            pname = sname = "Values Scan";
            break;
        case T_CteScan:
            pname = sname = "CTE Scan";
            break;
        case T_WorkTableScan:
            pname = sname = "WorkTable Scan";
            break;
        case T_ForeignScan:
            pname = sname = "Foreign Scan";
            break;
        case T_Material:
            pname = sname = "Materialize";
            break;
        case T_Sort:
            pname = sname = "Sort";
            break;
        case T_Group:
            pname = sname = "Group";
            break;
        case T_Agg:
            sname = "Aggregate";
            switch (((Agg *) plan)->aggstrategy)
            {
                case AGG_PLAIN:
                    pname = "Aggregate";
                    strategy = "Plain";
                    break;
                case AGG_SORTED:
                    pname = "GroupAggregate";
                    strategy = "Sorted";
                    break;
                case AGG_HASHED:
                    pname = "HashAggregate";
                    strategy = "Hashed";
                    break;
                default:
                    pname = "Aggregate ???";
                    strategy = "???";
                    break;
            }
            break;
        case T_WindowAgg:
            pname = sname = "WindowAgg";
            break;
        case T_Unique:
            pname = sname = "Unique";
            break;
        case T_SetOp:
            sname = "SetOp";
            switch (((SetOp *) plan)->strategy)
            {
                case SETOP_SORTED:
                    pname = "SetOp";
                    strategy = "Sorted";
                    break;
                case SETOP_HASHED:
                    pname = "HashSetOp";
                    strategy = "Hashed";
                    break;
                default:
                    pname = "SetOp ???";
                    strategy = "???";
                    break;
            }
            break;
        case T_LockRows:
            pname = sname = "LockRows";
            break;
        case T_Limit:
            pname = sname = "Limit";
            break;
        case T_Hash:
            pname = sname = "Hash";
            break;
        default:
            pname = sname = "???";
            break;
    }

    ExplainOpenGroup("Plan",
                     relationship ? NULL : "Plan",
                     true, es);

    if (es->format == EXPLAIN_FORMAT_TEXT)
    {
        if (plan_name)
        {
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str, "%s\n", plan_name);
            es->indent++;
        }
        if (es->indent)
        {
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfoString(es->str, "->  ");
            es->indent += 2;
        }
        appendStringInfoString(es->str, pname);
        es->indent++;
    }
    else
    {
        ExplainPropertyText("Node Type", sname, es);
        if (strategy)
            ExplainPropertyText("Strategy", strategy, es);
        if (operation)
            ExplainPropertyText("Operation", operation, es);
        if (relationship)
            ExplainPropertyText("Parent Relationship", relationship, es);
        if (plan_name)
            ExplainPropertyText("Subplan Name", plan_name, es);
    }

    switch (nodeTag(plan))
    {
        case T_SeqScan:
        case T_BitmapHeapScan:
        case T_TidScan:
        case T_SubqueryScan:
        case T_FunctionScan:
        case T_ValuesScan:
        case T_CteScan:
        case T_WorkTableScan:
        case T_ForeignScan:
            ExplainScanTarget((Scan *) plan, es);
            break;
        case T_IndexScan:
            {
                IndexScan  *indexscan = (IndexScan *) plan;

                ExplainIndexScanDetails(indexscan->indexid,
                                        indexscan->indexorderdir,
                                        es);
                ExplainScanTarget((Scan *) indexscan, es);
            }
            break;
        case T_IndexOnlyScan:
            {
                IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;

                ExplainIndexScanDetails(indexonlyscan->indexid,
                                        indexonlyscan->indexorderdir,
                                        es);
                ExplainScanTarget((Scan *) indexonlyscan, es);
            }
            break;
        case T_BitmapIndexScan:
            {
                BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
                const char *indexname =
                explain_get_index_name(bitmapindexscan->indexid);

                if (es->format == EXPLAIN_FORMAT_TEXT)
                    appendStringInfo(es->str, " on %s", indexname);
                else
                    ExplainPropertyText("Index Name", indexname, es);
            }
            break;
        case T_ModifyTable:
            ExplainModifyTarget((ModifyTable *) plan, es);
            break;
        case T_NestLoop:
        case T_MergeJoin:
        case T_HashJoin:
            {
                const char *jointype;

                switch (((Join *) plan)->jointype)
                {
                    case JOIN_INNER:
                        jointype = "Inner";
                        break;
                    case JOIN_LEFT:
                        jointype = "Left";
                        break;
                    case JOIN_FULL:
                        jointype = "Full";
                        break;
                    case JOIN_RIGHT:
                        jointype = "Right";
                        break;
                    case JOIN_SEMI:
                        jointype = "Semi";
                        break;
                    case JOIN_ANTI:
                        jointype = "Anti";
                        break;
                    default:
                        jointype = "???";
                        break;
                }
                if (es->format == EXPLAIN_FORMAT_TEXT)
                {
                    /*
                     * For historical reasons, the join type is interpolated
                     * into the node type name...
                     */
                    if (((Join *) plan)->jointype != JOIN_INNER)
                        appendStringInfo(es->str, " %s Join", jointype);
                    else if (!IsA(plan, NestLoop))
                        appendStringInfo(es->str, " Join");
                }
                else
                    ExplainPropertyText("Join Type", jointype, es);
            }
            break;
        case T_SetOp:
            {
                const char *setopcmd;

                switch (((SetOp *) plan)->cmd)
                {
                    case SETOPCMD_INTERSECT:
                        setopcmd = "Intersect";
                        break;
                    case SETOPCMD_INTERSECT_ALL:
                        setopcmd = "Intersect All";
                        break;
                    case SETOPCMD_EXCEPT:
                        setopcmd = "Except";
                        break;
                    case SETOPCMD_EXCEPT_ALL:
                        setopcmd = "Except All";
                        break;
                    default:
                        setopcmd = "???";
                        break;
                }
                if (es->format == EXPLAIN_FORMAT_TEXT)
                    appendStringInfo(es->str, " %s", setopcmd);
                else
                    ExplainPropertyText("Command", setopcmd, es);
            }
            break;
        default:
            break;
    }

    if (es->costs)
    {
        if (es->format == EXPLAIN_FORMAT_TEXT)
        {
            appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
                             plan->startup_cost, plan->total_cost,
                             plan->plan_rows, plan->plan_width);
        }
        else
        {
            ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
            ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
            ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
            ExplainPropertyInteger("Plan Width", plan->plan_width, es);
        }
    }

    /*
     * We have to forcibly clean up the instrumentation state because we
     * haven't done ExecutorEnd yet.  This is pretty grotty ...
     */
    if (planstate->instrument)
        InstrEndLoop(planstate->instrument);

    if (planstate->instrument && planstate->instrument->nloops > 0)
    {
        double      nloops = planstate->instrument->nloops;
        double      startup_sec = 1000.0 * planstate->instrument->startup / nloops;
        double      total_sec = 1000.0 * planstate->instrument->total / nloops;
        double      rows = planstate->instrument->ntuples / nloops;

        if (es->format == EXPLAIN_FORMAT_TEXT)
        {
            if (planstate->instrument->need_timer)
                appendStringInfo(es->str,
                            " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
                                 startup_sec, total_sec, rows, nloops);
            else
                appendStringInfo(es->str,
                                 " (actual rows=%.0f loops=%.0f)",
                                 rows, nloops);
        }
        else
        {
            if (planstate->instrument->need_timer)
            {
                ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
                ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
            }
            ExplainPropertyFloat("Actual Rows", rows, 0, es);
            ExplainPropertyFloat("Actual Loops", nloops, 0, es);
        }
    }
    else if (es->analyze)
    {

        if (es->format == EXPLAIN_FORMAT_TEXT)
            appendStringInfo(es->str, " (never executed)");
        else if (planstate->instrument->need_timer)
        {
            ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
            ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
        }
        else
        {
            ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
            ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
        }

    }

    /* in text format, first line ends here */
    if (es->format == EXPLAIN_FORMAT_TEXT)
        appendStringInfoChar(es->str, '\n');

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

    /* quals, sort keys, etc */
    switch (nodeTag(plan))
    {
        case T_IndexScan:
            show_scan_qual(((IndexScan *) plan)->indexqualorig,
                           "Index Cond", planstate, ancestors, es);
            if (((IndexScan *) plan)->indexqualorig)
                show_instrumentation_count("Rows Removed by Index Recheck", 2,
                                           planstate, es);
            show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
                           "Order By", planstate, ancestors, es);
            show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            break;
        case T_IndexOnlyScan:
            show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
                           "Index Cond", planstate, ancestors, es);
            if (((IndexOnlyScan *) plan)->indexqual)
                show_instrumentation_count("Rows Removed by Index Recheck", 2,
                                           planstate, es);
            show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
                           "Order By", planstate, ancestors, es);
            show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            if (es->analyze)
                ExplainPropertyLong("Heap Fetches",
                   ((IndexOnlyScanState *) planstate)->ioss_HeapFetches, es);
            break;
        case T_BitmapIndexScan:
            show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
                           "Index Cond", planstate, ancestors, es);
            break;
        case T_BitmapHeapScan:
            show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
                           "Recheck Cond", planstate, ancestors, es);
            if (((BitmapHeapScan *) plan)->bitmapqualorig)
                show_instrumentation_count("Rows Removed by Index Recheck", 2,
                                           planstate, es);
            /* FALL THRU */
        case T_SeqScan:
        case T_ValuesScan:
        case T_CteScan:
        case T_WorkTableScan:
        case T_SubqueryScan:
            show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            break;
        case T_FunctionScan:
            if (es->verbose)
                show_expression(((FunctionScan *) plan)->funcexpr,
                                "Function Call", planstate, ancestors,
                                es->verbose, es);
            show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            break;
        case T_TidScan:
            {
                /*
                 * The tidquals list has OR semantics, so be sure to show it
                 * as an OR condition.
                 */
                List       *tidquals = ((TidScan *) plan)->tidquals;

                if (list_length(tidquals) > 1)
                    tidquals = list_make1(make_orclause(tidquals));
                show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
                show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
                if (plan->qual)
                    show_instrumentation_count("Rows Removed by Filter", 1,
                                               planstate, es);
            }
            break;
        case T_ForeignScan:
            show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            show_foreignscan_info((ForeignScanState *) planstate, es);
            break;
        case T_NestLoop:
            show_upper_qual(((NestLoop *) plan)->join.joinqual,
                            "Join Filter", planstate, ancestors, es);
            if (((NestLoop *) plan)->join.joinqual)
                show_instrumentation_count("Rows Removed by Join Filter", 1,
                                           planstate, es);
            show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 2,
                                           planstate, es);
            break;
        case T_MergeJoin:
            show_upper_qual(((MergeJoin *) plan)->mergeclauses,
                            "Merge Cond", planstate, ancestors, es);
            show_upper_qual(((MergeJoin *) plan)->join.joinqual,
                            "Join Filter", planstate, ancestors, es);
            if (((MergeJoin *) plan)->join.joinqual)
                show_instrumentation_count("Rows Removed by Join Filter", 1,
                                           planstate, es);
            show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 2,
                                           planstate, es);
            break;
        case T_HashJoin:
            show_upper_qual(((HashJoin *) plan)->hashclauses,
                            "Hash Cond", planstate, ancestors, es);
            show_upper_qual(((HashJoin *) plan)->join.joinqual,
                            "Join Filter", planstate, ancestors, es);
            if (((HashJoin *) plan)->join.joinqual)
                show_instrumentation_count("Rows Removed by Join Filter", 1,
                                           planstate, es);
            show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 2,
                                           planstate, es);
            break;
        case T_Agg:
        case T_Group:
            show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            break;
        case T_Sort:
            show_sort_keys((SortState *) planstate, ancestors, es);
            show_sort_info((SortState *) planstate, es);
            break;
        case T_MergeAppend:
            show_merge_append_keys((MergeAppendState *) planstate,
                                   ancestors, es);
            break;
        case T_Result:
            show_upper_qual((List *) ((Result *) plan)->resconstantqual,
                            "One-Time Filter", planstate, ancestors, es);
            show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
            if (plan->qual)
                show_instrumentation_count("Rows Removed by Filter", 1,
                                           planstate, es);
            break;
        case T_ModifyTable:
            show_modifytable_info((ModifyTableState *) planstate, es);
            break;
        case T_Hash:
            show_hash_info((HashState *) planstate, es);
            break;
        default:
            break;
    }

    /* Show buffer usage */
    if (es->buffers)
    {
        const BufferUsage *usage = &planstate->instrument->bufusage;

        if (es->format == EXPLAIN_FORMAT_TEXT)
        {
            bool        has_shared = (usage->shared_blks_hit > 0 ||
                                      usage->shared_blks_read > 0 ||
                                      usage->shared_blks_dirtied > 0 ||
                                      usage->shared_blks_written > 0);
            bool        has_local = (usage->local_blks_hit > 0 ||
                                     usage->local_blks_read > 0 ||
                                     usage->local_blks_dirtied > 0 ||
                                     usage->local_blks_written > 0);
            bool        has_temp = (usage->temp_blks_read > 0 ||
                                    usage->temp_blks_written > 0);
            bool        has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
                                 !INSTR_TIME_IS_ZERO(usage->blk_write_time));

            /* Show only positive counter values. */
            if (has_shared || has_local || has_temp)
            {
                appendStringInfoSpaces(es->str, es->indent * 2);
                appendStringInfoString(es->str, "Buffers:");

                if (has_shared)
                {
                    appendStringInfoString(es->str, " shared");
                    if (usage->shared_blks_hit > 0)
                        appendStringInfo(es->str, " hit=%ld",
                                         usage->shared_blks_hit);
                    if (usage->shared_blks_read > 0)
                        appendStringInfo(es->str, " read=%ld",
                                         usage->shared_blks_read);
                    if (usage->shared_blks_dirtied > 0)
                        appendStringInfo(es->str, " dirtied=%ld",
                                         usage->shared_blks_dirtied);
                    if (usage->shared_blks_written > 0)
                        appendStringInfo(es->str, " written=%ld",
                                         usage->shared_blks_written);
                    if (has_local || has_temp)
                        appendStringInfoChar(es->str, ',');
                }
                if (has_local)
                {
                    appendStringInfoString(es->str, " local");
                    if (usage->local_blks_hit > 0)
                        appendStringInfo(es->str, " hit=%ld",
                                         usage->local_blks_hit);
                    if (usage->local_blks_read > 0)
                        appendStringInfo(es->str, " read=%ld",
                                         usage->local_blks_read);
                    if (usage->local_blks_dirtied > 0)
                        appendStringInfo(es->str, " dirtied=%ld",
                                         usage->local_blks_dirtied);
                    if (usage->local_blks_written > 0)
                        appendStringInfo(es->str, " written=%ld",
                                         usage->local_blks_written);
                    if (has_temp)
                        appendStringInfoChar(es->str, ',');
                }
                if (has_temp)
                {
                    appendStringInfoString(es->str, " temp");
                    if (usage->temp_blks_read > 0)
                        appendStringInfo(es->str, " read=%ld",
                                         usage->temp_blks_read);
                    if (usage->temp_blks_written > 0)
                        appendStringInfo(es->str, " written=%ld",
                                         usage->temp_blks_written);
                }
                appendStringInfoChar(es->str, '\n');
            }

            /* As above, show only positive counter values. */
            if (has_timing)
            {
                appendStringInfoSpaces(es->str, es->indent * 2);
                appendStringInfoString(es->str, "I/O Timings:");
                if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
                    appendStringInfo(es->str, " read=%0.3f",
                              INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
                if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
                    appendStringInfo(es->str, " write=%0.3f",
                             INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
                appendStringInfoChar(es->str, '\n');
            }
        }
        else
        {
            ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
            ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
            ExplainPropertyLong("Shared Dirtied Blocks", usage->shared_blks_dirtied, es);
            ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
            ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
            ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
            ExplainPropertyLong("Local Dirtied Blocks", usage->local_blks_dirtied, es);
            ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
            ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
            ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
            ExplainPropertyFloat("I/O Read Time", INSTR_TIME_GET_MILLISEC(usage->blk_read_time), 3, es);
            ExplainPropertyFloat("I/O Write Time", INSTR_TIME_GET_MILLISEC(usage->blk_write_time), 3, es);
        }
    }

    /* Get ready to display the child plans */
    haschildren = planstate->initPlan ||
        outerPlanState(planstate) ||
        innerPlanState(planstate) ||
        IsA(plan, ModifyTable) ||
        IsA(plan, Append) ||
        IsA(plan, MergeAppend) ||
        IsA(plan, BitmapAnd) ||
        IsA(plan, BitmapOr) ||
        IsA(plan, SubqueryScan) ||
        planstate->subPlan;
    if (haschildren)
    {
        ExplainOpenGroup("Plans", "Plans", false, es);
        /* Pass current PlanState as head of ancestors list for children */
        ancestors = lcons(planstate, ancestors);
    }

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

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

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

    /* special child plans */
    switch (nodeTag(plan))
    {
        case T_ModifyTable:
            ExplainMemberNodes(((ModifyTable *) plan)->plans,
                               ((ModifyTableState *) planstate)->mt_plans,
                               ancestors, es);
            break;
        case T_Append:
            ExplainMemberNodes(((Append *) plan)->appendplans,
                               ((AppendState *) planstate)->appendplans,
                               ancestors, es);
            break;
        case T_MergeAppend:
            ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
                               ((MergeAppendState *) planstate)->mergeplans,
                               ancestors, es);
            break;
        case T_BitmapAnd:
            ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
                               ((BitmapAndState *) planstate)->bitmapplans,
                               ancestors, es);
            break;
        case T_BitmapOr:
            ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
                               ((BitmapOrState *) planstate)->bitmapplans,
                               ancestors, es);
            break;
        case T_SubqueryScan:
            ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
                        "Subquery", NULL, es);
            break;
        default:
            break;
    }

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

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

    /* in text format, undo whatever indentation we added */
    if (es->format == EXPLAIN_FORMAT_TEXT)
        es->indent = save_indent;

    ExplainCloseGroup("Plan",
                      relationship ? NULL : "Plan",
                      true, es);
}

void ExplainOnePlan ( PlannedStmt plannedstmt,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params 
)

Definition at line 399 of file explain.c.

References ExplainState::analyze, appendStringInfo(), ExplainState::buffers, CommandCounterIncrement(), CreateIntoRelDestReceiver(), CreateQueryDesc(), elapsed_time(), EState::es_num_result_relations, EState::es_result_relations, EState::es_trig_target_relations, QueryDesc::estate, ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPrintPlan(), ExplainPropertyFloat(), ExplainState::format, FreeQueryDesc(), GetActiveSnapshot(), GetIntoRelEFlags(), INSTR_TIME_SET_CURRENT, InvalidSnapshot, lfirst, None_Receiver, NULL, PopActiveSnapshot(), PushCopiedSnapshot(), report_triggers(), IntoClause::skipData, ExplainState::str, ExplainState::timing, and UpdateActiveSnapshotCommandId().

Referenced by ExplainExecuteQuery(), and ExplainOneQuery().

{
    DestReceiver *dest;
    QueryDesc  *queryDesc;
    instr_time  starttime;
    double      totaltime = 0;
    int         eflags;
    int         instrument_option = 0;

    if (es->analyze && es->timing)
        instrument_option |= INSTRUMENT_TIMER;
    else if (es->analyze)
        instrument_option |= INSTRUMENT_ROWS;

    if (es->buffers)
        instrument_option |= INSTRUMENT_BUFFERS;

    INSTR_TIME_SET_CURRENT(starttime);

    /*
     * Use a snapshot with an updated command ID to ensure this query sees
     * results of any previously executed queries.
     */
    PushCopiedSnapshot(GetActiveSnapshot());
    UpdateActiveSnapshotCommandId();

    /*
     * Normally we discard the query's output, but if explaining CREATE TABLE
     * AS, we'd better use the appropriate tuple receiver.
     */
    if (into)
        dest = CreateIntoRelDestReceiver(into);
    else
        dest = None_Receiver;

    /* Create a QueryDesc for the query */
    queryDesc = CreateQueryDesc(plannedstmt, queryString,
                                GetActiveSnapshot(), InvalidSnapshot,
                                dest, params, instrument_option);

    /* Select execution options */
    if (es->analyze)
        eflags = 0;             /* default run-to-completion flags */
    else
        eflags = EXEC_FLAG_EXPLAIN_ONLY;
    if (into)
        eflags |= GetIntoRelEFlags(into);

    /* call ExecutorStart to prepare the plan for execution */
    ExecutorStart(queryDesc, eflags);

    /* Execute the plan for statistics if asked for */
    if (es->analyze)
    {
        ScanDirection dir;

        /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
        if (into && into->skipData)
            dir = NoMovementScanDirection;
        else
            dir = ForwardScanDirection;

        /* run the plan */
        ExecutorRun(queryDesc, dir, 0L);

        /* run cleanup too */
        ExecutorFinish(queryDesc);

        /* We can't run ExecutorEnd 'till we're done printing the stats... */
        totaltime += elapsed_time(&starttime);
    }

    ExplainOpenGroup("Query", NULL, true, es);

    /* Create textual dump of plan tree */
    ExplainPrintPlan(es, queryDesc);

    /* Print info about runtime of triggers */
    if (es->analyze)
    {
        ResultRelInfo *rInfo;
        bool        show_relname;
        int         numrels = queryDesc->estate->es_num_result_relations;
        List       *targrels = queryDesc->estate->es_trig_target_relations;
        int         nr;
        ListCell   *l;

        ExplainOpenGroup("Triggers", "Triggers", false, es);

        show_relname = (numrels > 1 || targrels != NIL);
        rInfo = queryDesc->estate->es_result_relations;
        for (nr = 0; nr < numrels; rInfo++, nr++)
            report_triggers(rInfo, show_relname, es);

        foreach(l, targrels)
        {
            rInfo = (ResultRelInfo *) lfirst(l);
            report_triggers(rInfo, show_relname, es);
        }

        ExplainCloseGroup("Triggers", "Triggers", false, es);
    }

    /*
     * Close down the query and free resources.  Include time for this in the
     * total runtime (although it should be pretty minimal).
     */
    INSTR_TIME_SET_CURRENT(starttime);

    ExecutorEnd(queryDesc);

    FreeQueryDesc(queryDesc);

    PopActiveSnapshot();

    /* We need a CCI just in case query expanded to multiple plans */
    if (es->analyze)
        CommandCounterIncrement();

    totaltime += elapsed_time(&starttime);

    if (es->analyze)
    {
        if (es->format == EXPLAIN_FORMAT_TEXT)
            appendStringInfo(es->str, "Total runtime: %.3f ms\n",
                             1000.0 * totaltime);
        else
            ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime,
                                 3, es);
    }

    ExplainCloseGroup("Query", NULL, true, es);
}

static void ExplainOneQuery ( Query query,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params 
) [static]

Definition at line 302 of file explain.c.

References CMD_UTILITY, Query::commandType, ExplainOnePlan(), ExplainOneQuery_hook, ExplainOneUtility(), pg_plan_query(), and Query::utilityStmt.

Referenced by ExplainOneUtility(), and ExplainQuery().

{
    /* planner will not cope with utility statements */
    if (query->commandType == CMD_UTILITY)
    {
        ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
        return;
    }

    /* if an advisor plugin is present, let it manage things */
    if (ExplainOneQuery_hook)
        (*ExplainOneQuery_hook) (query, into, es, queryString, params);
    else
    {
        PlannedStmt *plan;

        /* plan the query */
        plan = pg_plan_query(query, 0, params);

        /* run it (if needed) and produce output */
        ExplainOnePlan(plan, into, es, queryString, params);
    }
}

void ExplainOneUtility ( Node utilityStmt,
IntoClause into,
ExplainState es,
const char *  queryString,
ParamListInfo  params 
)

Definition at line 339 of file explain.c.

References appendStringInfoString(), Assert, copyObject(), EXPLAIN_FORMAT_TEXT, ExplainDummyGroup(), ExplainExecuteQuery(), ExplainOneQuery(), ExplainState::format, CreateTableAsStmt::into, IsA, linitial, list_length(), NULL, CreateTableAsStmt::query, QueryRewrite(), and ExplainState::str.

Referenced by ExplainExecuteQuery(), and ExplainOneQuery().

{
    if (utilityStmt == NULL)
        return;

    if (IsA(utilityStmt, CreateTableAsStmt))
    {
        /*
         * We have to rewrite the contained SELECT and then pass it back to
         * ExplainOneQuery.  It's probably not really necessary to copy the
         * contained parsetree another time, but let's be safe.
         */
        CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
        List       *rewritten;

        Assert(IsA(ctas->query, Query));
        rewritten = QueryRewrite((Query *) copyObject(ctas->query));
        Assert(list_length(rewritten) == 1);
        ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es,
                        queryString, params);
    }
    else if (IsA(utilityStmt, ExecuteStmt))
        ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
                            queryString, params);
    else if (IsA(utilityStmt, NotifyStmt))
    {
        if (es->format == EXPLAIN_FORMAT_TEXT)
            appendStringInfoString(es->str, "NOTIFY\n");
        else
            ExplainDummyGroup("Notify", NULL, es);
    }
    else
    {
        if (es->format == EXPLAIN_FORMAT_TEXT)
            appendStringInfoString(es->str,
                              "Utility statements have no plan structure\n");
        else
            ExplainDummyGroup("Utility Statement", NULL, es);
    }
}

static void ExplainOpenGroup ( const char *  objtype,
const char *  labelname,
bool  labeled,
ExplainState es 
) [static]

Definition at line 2304 of file explain.c.

References appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), escape_json(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::grouping_stack, ExplainState::indent, lcons_int(), ExplainState::str, and X_OPENING.

Referenced by ExplainNode(), ExplainOnePlan(), and report_triggers().

{
    switch (es->format)
    {
        case EXPLAIN_FORMAT_TEXT:
            /* nothing to do */
            break;

        case EXPLAIN_FORMAT_XML:
            ExplainXMLTag(objtype, X_OPENING, es);
            es->indent++;
            break;

        case EXPLAIN_FORMAT_JSON:
            ExplainJSONLineEnding(es);
            appendStringInfoSpaces(es->str, 2 * es->indent);
            if (labelname)
            {
                escape_json(es->str, labelname);
                appendStringInfoString(es->str, ": ");
            }
            appendStringInfoChar(es->str, labeled ? '{' : '[');

            /*
             * In JSON format, the grouping_stack is an integer list.  0 means
             * we've emitted nothing at this grouping level, 1 means we've
             * emitted something (and so the next item needs a comma). See
             * ExplainJSONLineEnding().
             */
            es->grouping_stack = lcons_int(0, es->grouping_stack);
            es->indent++;
            break;

        case EXPLAIN_FORMAT_YAML:

            /*
             * In YAML format, the grouping stack is an integer list.  0 means
             * we've emitted nothing at this grouping level AND this grouping
             * level is unlabelled and must be marked with "- ".  See
             * ExplainYAMLLineStarting().
             */
            ExplainYAMLLineStarting(es);
            if (labelname)
            {
                appendStringInfo(es->str, "%s: ", labelname);
                es->grouping_stack = lcons_int(1, es->grouping_stack);
            }
            else
            {
                appendStringInfoString(es->str, "- ");
                es->grouping_stack = lcons_int(0, es->grouping_stack);
            }
            es->indent++;
            break;
    }
}

static void ExplainPreScanMemberNodes ( List plans,
PlanState **  planstates,
Bitmapset **  rels_used 
) [static]

Definition at line 752 of file explain.c.

References ExplainPreScanNode(), and list_length().

Referenced by ExplainPreScanNode().

{
    int         nplans = list_length(plans);
    int         j;

    for (j = 0; j < nplans; j++)
        ExplainPreScanNode(planstates[j], rels_used);
}

static void ExplainPreScanNode ( PlanState planstate,
Bitmapset **  rels_used 
) [static]

Definition at line 662 of file explain.c.

References bms_add_member(), ExplainPreScanMemberNodes(), ExplainPreScanSubPlans(), PlanState::initPlan, innerPlanState, linitial_int, nodeTag, outerPlanState, PlanState::plan, PlanState::subPlan, T_Append, T_BitmapAnd, T_BitmapHeapScan, T_BitmapOr, T_CteScan, T_ForeignScan, T_FunctionScan, T_IndexOnlyScan, T_IndexScan, T_MergeAppend, T_ModifyTable, T_SeqScan, T_SubqueryScan, T_TidScan, T_ValuesScan, and T_WorkTableScan.

Referenced by ExplainPreScanMemberNodes(), ExplainPreScanSubPlans(), and ExplainPrintPlan().

{
    Plan       *plan = planstate->plan;

    switch (nodeTag(plan))
    {
        case T_SeqScan:
        case T_IndexScan:
        case T_IndexOnlyScan:
        case T_BitmapHeapScan:
        case T_TidScan:
        case T_SubqueryScan:
        case T_FunctionScan:
        case T_ValuesScan:
        case T_CteScan:
        case T_WorkTableScan:
        case T_ForeignScan:
            *rels_used = bms_add_member(*rels_used,
                                        ((Scan *) plan)->scanrelid);
            break;
        case T_ModifyTable:
            /* cf ExplainModifyTarget */
            *rels_used = bms_add_member(*rels_used,
                      linitial_int(((ModifyTable *) plan)->resultRelations));
            break;
        default:
            break;
    }

    /* initPlan-s */
    if (planstate->initPlan)
        ExplainPreScanSubPlans(planstate->initPlan, rels_used);

    /* lefttree */
    if (outerPlanState(planstate))
        ExplainPreScanNode(outerPlanState(planstate), rels_used);

    /* righttree */
    if (innerPlanState(planstate))
        ExplainPreScanNode(innerPlanState(planstate), rels_used);

    /* special child plans */
    switch (nodeTag(plan))
    {
        case T_ModifyTable:
            ExplainPreScanMemberNodes(((ModifyTable *) plan)->plans,
                                  ((ModifyTableState *) planstate)->mt_plans,
                                      rels_used);
            break;
        case T_Append:
            ExplainPreScanMemberNodes(((Append *) plan)->appendplans,
                                    ((AppendState *) planstate)->appendplans,
                                      rels_used);
            break;
        case T_MergeAppend:
            ExplainPreScanMemberNodes(((MergeAppend *) plan)->mergeplans,
                                ((MergeAppendState *) planstate)->mergeplans,
                                      rels_used);
            break;
        case T_BitmapAnd:
            ExplainPreScanMemberNodes(((BitmapAnd *) plan)->bitmapplans,
                                 ((BitmapAndState *) planstate)->bitmapplans,
                                      rels_used);
            break;
        case T_BitmapOr:
            ExplainPreScanMemberNodes(((BitmapOr *) plan)->bitmapplans,
                                  ((BitmapOrState *) planstate)->bitmapplans,
                                      rels_used);
            break;
        case T_SubqueryScan:
            ExplainPreScanNode(((SubqueryScanState *) planstate)->subplan,
                               rels_used);
            break;
        default:
            break;
    }

    /* subPlan-s */
    if (planstate->subPlan)
        ExplainPreScanSubPlans(planstate->subPlan, rels_used);
}

static void ExplainPreScanSubPlans ( List plans,
Bitmapset **  rels_used 
) [static]

Definition at line 766 of file explain.c.

References ExplainPreScanNode(), lfirst, and SubPlanState::planstate.

Referenced by ExplainPreScanNode().

{
    ListCell   *lst;

    foreach(lst, plans)
    {
        SubPlanState *sps = (SubPlanState *) lfirst(lst);

        ExplainPreScanNode(sps->planstate, rels_used);
    }
}

void ExplainPrintPlan ( ExplainState es,
QueryDesc queryDesc 
)
static void ExplainProperty ( const char *  qlabel,
const char *  value,
bool  numeric,
ExplainState es 
) [static]

Definition at line 2201 of file explain.c.

References appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), escape_json(), escape_xml(), escape_yaml(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::indent, pfree(), ExplainState::str, X_CLOSING, X_NOWHITESPACE, and X_OPENING.

Referenced by ExplainPropertyFloat(), ExplainPropertyInteger(), ExplainPropertyLong(), and ExplainPropertyText().

{
    switch (es->format)
    {
        case EXPLAIN_FORMAT_TEXT:
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str, "%s: %s\n", qlabel, value);
            break;

        case EXPLAIN_FORMAT_XML:
            {
                char       *str;

                appendStringInfoSpaces(es->str, es->indent * 2);
                ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
                str = escape_xml(value);
                appendStringInfoString(es->str, str);
                pfree(str);
                ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
                appendStringInfoChar(es->str, '\n');
            }
            break;

        case EXPLAIN_FORMAT_JSON:
            ExplainJSONLineEnding(es);
            appendStringInfoSpaces(es->str, es->indent * 2);
            escape_json(es->str, qlabel);
            appendStringInfoString(es->str, ": ");
            if (numeric)
                appendStringInfoString(es->str, value);
            else
                escape_json(es->str, value);
            break;

        case EXPLAIN_FORMAT_YAML:
            ExplainYAMLLineStarting(es);
            appendStringInfo(es->str, "%s: ", qlabel);
            if (numeric)
                appendStringInfoString(es->str, value);
            else
                escape_yaml(es->str, value);
            break;
    }
}

void ExplainPropertyFloat ( const char *  qlabel,
double  value,
int  ndigits,
ExplainState es 
)

Definition at line 2285 of file explain.c.

References buf, ExplainProperty(), and snprintf().

Referenced by ExplainNode(), ExplainOnePlan(), report_triggers(), and show_instrumentation_count().

{
    char        buf[256];

    snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
    ExplainProperty(qlabel, buf, true, es);
}

void ExplainPropertyInteger ( const char *  qlabel,
int  value,
ExplainState es 
)

Definition at line 2260 of file explain.c.

References buf, ExplainProperty(), and snprintf().

Referenced by ExplainNode().

{
    char        buf[32];

    snprintf(buf, sizeof(buf), "%d", value);
    ExplainProperty(qlabel, buf, true, es);
}

void ExplainPropertyList ( const char *  qlabel,
List data,
ExplainState es 
)

Definition at line 2126 of file explain.c.

References appendStringInfo(), appendStringInfoChar(), appendStringInfoSpaces(), appendStringInfoString(), escape_json(), escape_xml(), escape_yaml(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainJSONLineEnding(), ExplainXMLTag(), ExplainYAMLLineStarting(), ExplainState::format, ExplainState::indent, lfirst, pfree(), ExplainState::str, X_CLOSING, and X_OPENING.

Referenced by show_plan_tlist(), and show_sort_keys_common().

{
    ListCell   *lc;
    bool        first = true;

    switch (es->format)
    {
        case EXPLAIN_FORMAT_TEXT:
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str, "%s: ", qlabel);
            foreach(lc, data)
            {
                if (!first)
                    appendStringInfoString(es->str, ", ");
                appendStringInfoString(es->str, (const char *) lfirst(lc));
                first = false;
            }
            appendStringInfoChar(es->str, '\n');
            break;

        case EXPLAIN_FORMAT_XML:
            ExplainXMLTag(qlabel, X_OPENING, es);
            foreach(lc, data)
            {
                char       *str;

                appendStringInfoSpaces(es->str, es->indent * 2 + 2);
                appendStringInfoString(es->str, "<Item>");
                str = escape_xml((const char *) lfirst(lc));
                appendStringInfoString(es->str, str);
                pfree(str);
                appendStringInfoString(es->str, "</Item>\n");
            }
            ExplainXMLTag(qlabel, X_CLOSING, es);
            break;

        case EXPLAIN_FORMAT_JSON:
            ExplainJSONLineEnding(es);
            appendStringInfoSpaces(es->str, es->indent * 2);
            escape_json(es->str, qlabel);
            appendStringInfoString(es->str, ": [");
            foreach(lc, data)
            {
                if (!first)
                    appendStringInfoString(es->str, ", ");
                escape_json(es->str, (const char *) lfirst(lc));
                first = false;
            }
            appendStringInfoChar(es->str, ']');
            break;

        case EXPLAIN_FORMAT_YAML:
            ExplainYAMLLineStarting(es);
            appendStringInfo(es->str, "%s: ", qlabel);
            foreach(lc, data)
            {
                appendStringInfoChar(es->str, '\n');
                appendStringInfoSpaces(es->str, es->indent * 2 + 2);
                appendStringInfoString(es->str, "- ");
                escape_yaml(es->str, (const char *) lfirst(lc));
            }
            break;
    }
}

void ExplainPropertyLong ( const char *  qlabel,
long  value,
ExplainState es 
)

Definition at line 2272 of file explain.c.

References buf, ExplainProperty(), and snprintf().

Referenced by ExplainNode(), fileExplainForeignScan(), show_hash_info(), and show_sort_info().

{
    char        buf[32];

    snprintf(buf, sizeof(buf), "%ld", value);
    ExplainProperty(qlabel, buf, true, es);
}

void ExplainPropertyText ( const char *  qlabel,
const char *  value,
ExplainState es 
)
void ExplainQuery ( ExplainStmt stmt,
const char *  queryString,
ParamListInfo  params,
DestReceiver dest 
)

Definition at line 118 of file explain.c.

References ExplainState::analyze, appendStringInfoString(), Assert, begin_tup_output_tupdesc(), ExplainState::buffers, copyObject(), ExplainState::costs, StringInfoData::data, defGetBoolean(), defGetString(), DefElem::defname, do_text_output_multiline(), do_text_output_oneline, end_tup_output(), ereport, errcode(), errmsg(), ERROR, EXPLAIN_FORMAT_TEXT, ExplainBeginOutput(), ExplainEndOutput(), ExplainInitState(), ExplainOneQuery(), ExplainResultDesc(), ExplainSeparatePlans(), ExplainState::format, ExplainState::indent, IsA, lfirst, lnext, NIL, NULL, ExplainStmt::options, pfree(), ExplainStmt::query, QueryRewrite(), ExplainState::str, ExplainState::timing, and ExplainState::verbose.

Referenced by standard_ProcessUtility().

{
    ExplainState es;
    TupOutputState *tstate;
    List       *rewritten;
    ListCell   *lc;
    bool        timing_set = false;

    /* Initialize ExplainState. */
    ExplainInitState(&es);

    /* Parse options list. */
    foreach(lc, stmt->options)
    {
        DefElem    *opt = (DefElem *) lfirst(lc);

        if (strcmp(opt->defname, "analyze") == 0)
            es.analyze = defGetBoolean(opt);
        else if (strcmp(opt->defname, "verbose") == 0)
            es.verbose = defGetBoolean(opt);
        else if (strcmp(opt->defname, "costs") == 0)
            es.costs = defGetBoolean(opt);
        else if (strcmp(opt->defname, "buffers") == 0)
            es.buffers = defGetBoolean(opt);
        else if (strcmp(opt->defname, "timing") == 0)
        {
            timing_set = true;
            es.timing = defGetBoolean(opt);
        }
        else if (strcmp(opt->defname, "format") == 0)
        {
            char       *p = defGetString(opt);

            if (strcmp(p, "text") == 0)
                es.format = EXPLAIN_FORMAT_TEXT;
            else if (strcmp(p, "xml") == 0)
                es.format = EXPLAIN_FORMAT_XML;
            else if (strcmp(p, "json") == 0)
                es.format = EXPLAIN_FORMAT_JSON;
            else if (strcmp(p, "yaml") == 0)
                es.format = EXPLAIN_FORMAT_YAML;
            else
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
                       opt->defname, p)));
        }
        else
            ereport(ERROR,
                    (errcode(ERRCODE_SYNTAX_ERROR),
                     errmsg("unrecognized EXPLAIN option \"%s\"",
                            opt->defname)));
    }

    if (es.buffers && !es.analyze)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));

    /* if the timing was not set explicitly, set default value */
    es.timing = (timing_set) ? es.timing : es.analyze;

    /* check that timing is used with EXPLAIN ANALYZE */
    if (es.timing && !es.analyze)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("EXPLAIN option TIMING requires ANALYZE")));

    /*
     * Parse analysis was done already, but we still have to run the rule
     * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
     * came straight from the parser, or suitable locks were acquired by
     * plancache.c.
     *
     * Because the rewriter and planner tend to scribble on the input, we make
     * a preliminary copy of the source querytree.  This prevents problems in
     * the case that the EXPLAIN is in a portal or plpgsql function and is
     * executed repeatedly.  (See also the same hack in DECLARE CURSOR and
     * PREPARE.)  XXX FIXME someday.
     */
    Assert(IsA(stmt->query, Query));
    rewritten = QueryRewrite((Query *) copyObject(stmt->query));

    /* emit opening boilerplate */
    ExplainBeginOutput(&es);

    if (rewritten == NIL)
    {
        /*
         * In the case of an INSTEAD NOTHING, tell at least that.  But in
         * non-text format, the output is delimited, so this isn't necessary.
         */
        if (es.format == EXPLAIN_FORMAT_TEXT)
            appendStringInfoString(es.str, "Query rewrites to nothing\n");
    }
    else
    {
        ListCell   *l;

        /* Explain every plan */
        foreach(l, rewritten)
        {
            ExplainOneQuery((Query *) lfirst(l), NULL, &es,
                            queryString, params);

            /* Separate plans with an appropriate separator */
            if (lnext(l) != NULL)
                ExplainSeparatePlans(&es);
        }
    }

    /* emit closing boilerplate */
    ExplainEndOutput(&es);
    Assert(es.indent == 0);

    /* output tuples */
    tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
    if (es.format == EXPLAIN_FORMAT_TEXT)
        do_text_output_multiline(tstate, es.str->data);
    else
        do_text_output_oneline(tstate, es.str->data);
    end_tup_output(tstate);

    pfree(es.str->data);
}

void ExplainQueryText ( ExplainState es,
QueryDesc queryDesc 
)

Definition at line 566 of file explain.c.

References ExplainPropertyText(), and QueryDesc::sourceText.

Referenced by explain_ExecutorEnd().

{
    if (queryDesc->sourceText)
        ExplainPropertyText("Query Text", queryDesc->sourceText, es);
}

TupleDesc ExplainResultDesc ( ExplainStmt stmt  ) 

Definition at line 263 of file explain.c.

References CreateTemplateTupleDesc(), defGetString(), DefElem::defname, lfirst, ExplainStmt::options, and TupleDescInitEntry().

Referenced by ExplainQuery(), and UtilityTupleDescriptor().

{
    TupleDesc   tupdesc;
    ListCell   *lc;
    Oid         result_type = TEXTOID;

    /* Check for XML format option */
    foreach(lc, stmt->options)
    {
        DefElem    *opt = (DefElem *) lfirst(lc);

        if (strcmp(opt->defname, "format") == 0)
        {
            char       *p = defGetString(opt);

            if (strcmp(p, "xml") == 0)
                result_type = XMLOID;
            else if (strcmp(p, "json") == 0)
                result_type = JSONOID;
            else
                result_type = TEXTOID;
            /* don't "break", as ExplainQuery will use the last value */
        }
    }

    /* Need a tuple descriptor representing a single TEXT or XML column */
    tupdesc = CreateTemplateTupleDesc(1, false);
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
                       result_type, -1, 0);
    return tupdesc;
}

static void ExplainScanTarget ( Scan plan,
ExplainState es 
) [static]

Definition at line 1925 of file explain.c.

References ExplainTargetRel().

Referenced by ExplainNode().

{
    ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
}

void ExplainSeparatePlans ( ExplainState es  ) 

Definition at line 2509 of file explain.c.

References appendStringInfoChar(), EXPLAIN_FORMAT_JSON, EXPLAIN_FORMAT_TEXT, EXPLAIN_FORMAT_XML, EXPLAIN_FORMAT_YAML, ExplainState::format, and ExplainState::str.

Referenced by ExplainExecuteQuery(), and ExplainQuery().

{
    switch (es->format)
    {
        case EXPLAIN_FORMAT_TEXT:
            /* add a blank line */
            appendStringInfoChar(es->str, '\n');
            break;

        case EXPLAIN_FORMAT_XML:
        case EXPLAIN_FORMAT_JSON:
        case EXPLAIN_FORMAT_YAML:
            /* nothing to do */
            break;
    }
}

static void ExplainSubPlans ( List plans,
List ancestors,
const char *  relationship,
ExplainState es 
) [static]

Definition at line 2106 of file explain.c.

References ExplainNode(), ExprState::expr, lfirst, SubPlan::plan_name, SubPlanState::planstate, and SubPlanState::xprstate.

Referenced by ExplainNode().

{
    ListCell   *lst;

    foreach(lst, plans)
    {
        SubPlanState *sps = (SubPlanState *) lfirst(lst);
        SubPlan    *sp = (SubPlan *) sps->xprstate.expr;

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

static void ExplainTargetRel ( Plan plan,
Index  rti,
ExplainState es 
) [static]

Definition at line 1952 of file explain.c.

References Alias::aliasname, appendStringInfo(), appendStringInfoString(), Assert, RangeTblEntry::eref, EXPLAIN_FORMAT_TEXT, ExplainPropertyText(), ExplainState::format, get_func_name(), get_namespace_name(), get_rel_name(), IsA, list_nth(), nodeTag, NULL, quote_identifier(), RangeTblEntry::relid, rt_fetch, ExplainState::rtable, ExplainState::rtable_names, RTE_CTE, RTE_FUNCTION, RTE_RELATION, RTE_VALUES, RangeTblEntry::rtekind, ExplainState::str, T_BitmapHeapScan, T_CteScan, T_ForeignScan, T_FunctionScan, T_IndexOnlyScan, T_IndexScan, T_ModifyTable, T_SeqScan, T_TidScan, T_ValuesScan, T_WorkTableScan, and ExplainState::verbose.

Referenced by ExplainModifyTarget(), and ExplainScanTarget().

{
    char       *objectname = NULL;
    char       *namespace = NULL;
    const char *objecttag = NULL;
    RangeTblEntry *rte;
    char       *refname;

    rte = rt_fetch(rti, es->rtable);
    refname = (char *) list_nth(es->rtable_names, rti - 1);
    if (refname == NULL)
        refname = rte->eref->aliasname;

    switch (nodeTag(plan))
    {
        case T_SeqScan:
        case T_IndexScan:
        case T_IndexOnlyScan:
        case T_BitmapHeapScan:
        case T_TidScan:
        case T_ForeignScan:
        case T_ModifyTable:
            /* Assert it's on a real relation */
            Assert(rte->rtekind == RTE_RELATION);
            objectname = get_rel_name(rte->relid);
            if (es->verbose)
                namespace = get_namespace_name(get_rel_namespace(rte->relid));
            objecttag = "Relation Name";
            break;
        case T_FunctionScan:
            {
                Node       *funcexpr;

                /* Assert it's on a RangeFunction */
                Assert(rte->rtekind == RTE_FUNCTION);

                /*
                 * If the expression is still a function call, we can get the
                 * real name of the function.  Otherwise, punt (this can
                 * happen if the optimizer simplified away the function call,
                 * for example).
                 */
                funcexpr = ((FunctionScan *) plan)->funcexpr;
                if (funcexpr && IsA(funcexpr, FuncExpr))
                {
                    Oid         funcid = ((FuncExpr *) funcexpr)->funcid;

                    objectname = get_func_name(funcid);
                    if (es->verbose)
                        namespace =
                            get_namespace_name(get_func_namespace(funcid));
                }
                objecttag = "Function Name";
            }
            break;
        case T_ValuesScan:
            Assert(rte->rtekind == RTE_VALUES);
            break;
        case T_CteScan:
            /* Assert it's on a non-self-reference CTE */
            Assert(rte->rtekind == RTE_CTE);
            Assert(!rte->self_reference);
            objectname = rte->ctename;
            objecttag = "CTE Name";
            break;
        case T_WorkTableScan:
            /* Assert it's on a self-reference CTE */
            Assert(rte->rtekind == RTE_CTE);
            Assert(rte->self_reference);
            objectname = rte->ctename;
            objecttag = "CTE Name";
            break;
        default:
            break;
    }

    if (es->format == EXPLAIN_FORMAT_TEXT)
    {
        appendStringInfoString(es->str, " on");
        if (namespace != NULL)
            appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
                             quote_identifier(objectname));
        else if (objectname != NULL)
            appendStringInfo(es->str, " %s", quote_identifier(objectname));
        if (objectname == NULL || strcmp(refname, objectname) != 0)
            appendStringInfo(es->str, " %s", quote_identifier(refname));
    }
    else
    {
        if (objecttag != NULL && objectname != NULL)
            ExplainPropertyText(objecttag, objectname, es);
        if (namespace != NULL)
            ExplainPropertyText("Schema", namespace, es);
        ExplainPropertyText("Alias", refname, es);
    }
}

static void ExplainXMLTag ( const char *  tagname,
int  flags,
ExplainState es 
) [static]

Definition at line 2537 of file explain.c.

References appendStringInfoCharMacro, appendStringInfoSpaces(), appendStringInfoString(), ExplainState::indent, ExplainState::str, X_CLOSE_IMMEDIATE, X_CLOSING, and X_NOWHITESPACE.

Referenced by ExplainCloseGroup(), ExplainDummyGroup(), ExplainOpenGroup(), ExplainProperty(), and ExplainPropertyList().

{
    const char *s;

    if ((flags & X_NOWHITESPACE) == 0)
        appendStringInfoSpaces(es->str, 2 * es->indent);
    appendStringInfoCharMacro(es->str, '<');
    if ((flags & X_CLOSING) != 0)
        appendStringInfoCharMacro(es->str, '/');
    for (s = tagname; *s; s++)
        appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
    if ((flags & X_CLOSE_IMMEDIATE) != 0)
        appendStringInfoString(es->str, " /");
    appendStringInfoCharMacro(es->str, '>');
    if ((flags & X_NOWHITESPACE) == 0)
        appendStringInfoCharMacro(es->str, '\n');
}

static void ExplainYAMLLineStarting ( ExplainState es  )  [static]
static void report_triggers ( ResultRelInfo rInfo,
bool  show_relname,
ExplainState es 
) [static]

Definition at line 577 of file explain.c.

References appendStringInfo(), appendStringInfoString(), EXPLAIN_FORMAT_TEXT, ExplainCloseGroup(), ExplainOpenGroup(), ExplainPropertyFloat(), ExplainPropertyText(), ExplainState::format, get_constraint_name(), InstrEndLoop(), Instrumentation::ntuples, NULL, TriggerDesc::numtriggers, OidIsValid, pfree(), RelationGetRelationName, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_TrigInstrument, ExplainState::str, Trigger::tgconstraint, Trigger::tgname, Instrumentation::total, TriggerDesc::triggers, and ExplainState::verbose.

Referenced by ExplainOnePlan().

{
    int         nt;

    if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
        return;
    for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
    {
        Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
        Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
        char       *relname;
        char       *conname = NULL;

        /* Must clean up instrumentation state */
        InstrEndLoop(instr);

        /*
         * We ignore triggers that were never invoked; they likely aren't
         * relevant to the current query type.
         */
        if (instr->ntuples == 0)
            continue;

        ExplainOpenGroup("Trigger", NULL, true, es);

        relname = RelationGetRelationName(rInfo->ri_RelationDesc);
        if (OidIsValid(trig->tgconstraint))
            conname = get_constraint_name(trig->tgconstraint);

        /*
         * In text format, we avoid printing both the trigger name and the
         * constraint name unless VERBOSE is specified.  In non-text formats
         * we just print everything.
         */
        if (es->format == EXPLAIN_FORMAT_TEXT)
        {
            if (es->verbose || conname == NULL)
                appendStringInfo(es->str, "Trigger %s", trig->tgname);
            else
                appendStringInfoString(es->str, "Trigger");
            if (conname)
                appendStringInfo(es->str, " for constraint %s", conname);
            if (show_relname)
                appendStringInfo(es->str, " on %s", relname);
            appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
                             1000.0 * instr->total, instr->ntuples);
        }
        else
        {
            ExplainPropertyText("Trigger Name", trig->tgname, es);
            if (conname)
                ExplainPropertyText("Constraint Name", conname, es);
            ExplainPropertyText("Relation", relname, es);
            ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
            ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
        }

        if (conname)
            pfree(conname);

        ExplainCloseGroup("Trigger", NULL, true, es);
    }
}

static void show_expression ( Node node,
const char *  qlabel,
PlanState planstate,
List ancestors,
bool  useprefix,
ExplainState es 
) [static]

Definition at line 1603 of file explain.c.

References deparse_context_for_planstate(), deparse_expression(), ExplainPropertyText(), ExplainState::rtable, and ExplainState::rtable_names.

Referenced by ExplainNode(), and show_qual().

{
    List       *context;
    char       *exprstr;

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

    /* Deparse the expression */
    exprstr = deparse_expression(node, context, useprefix, false);

    /* And add to es->str */
    ExplainPropertyText(qlabel, exprstr, es);
}

static void show_foreignscan_info ( ForeignScanState fsstate,
ExplainState es 
) [static]

Definition at line 1847 of file explain.c.

References FdwRoutine::ExplainForeignScan, ForeignScanState::fdwroutine, and NULL.

Referenced by ExplainNode().

{
    FdwRoutine *fdwroutine = fsstate->fdwroutine;

    /* Let the FDW emit whatever fields it wants */
    if (fdwroutine->ExplainForeignScan != NULL)
        fdwroutine->ExplainForeignScan(fsstate, es);
}

static void show_hash_info ( HashState hashstate,
ExplainState es 
) [static]

Definition at line 1774 of file explain.c.

References appendStringInfo(), appendStringInfoSpaces(), Assert, EXPLAIN_FORMAT_TEXT, ExplainPropertyLong(), ExplainState::format, HashState::hashtable, ExplainState::indent, IsA, HashJoinTableData::nbatch, HashJoinTableData::nbatch_original, HashJoinTableData::nbuckets, HashJoinTableData::spacePeak, and ExplainState::str.

Referenced by ExplainNode().

{
    HashJoinTable hashtable;

    Assert(IsA(hashstate, HashState));
    hashtable = hashstate->hashtable;

    if (hashtable)
    {
        long        spacePeakKb = (hashtable->spacePeak + 1023) / 1024;

        if (es->format != EXPLAIN_FORMAT_TEXT)
        {
            ExplainPropertyLong("Hash Buckets", hashtable->nbuckets, es);
            ExplainPropertyLong("Hash Batches", hashtable->nbatch, es);
            ExplainPropertyLong("Original Hash Batches",
                                hashtable->nbatch_original, es);
            ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es);
        }
        else if (hashtable->nbatch_original != hashtable->nbatch)
        {
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str,
            "Buckets: %d  Batches: %d (originally %d)  Memory Usage: %ldkB\n",
                             hashtable->nbuckets, hashtable->nbatch,
                             hashtable->nbatch_original, spacePeakKb);
        }
        else
        {
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str,
                           "Buckets: %d  Batches: %d  Memory Usage: %ldkB\n",
                             hashtable->nbuckets, hashtable->nbatch,
                             spacePeakKb);
        }
    }
}

static void show_instrumentation_count ( const char *  qlabel,
int  which,
PlanState planstate,
ExplainState es 
) [static]

Definition at line 1818 of file explain.c.

References ExplainState::analyze, EXPLAIN_FORMAT_TEXT, ExplainPropertyFloat(), ExplainState::format, PlanState::instrument, Instrumentation::nfiltered1, Instrumentation::nfiltered2, and Instrumentation::nloops.

Referenced by ExplainNode().

{
    double      nfiltered;
    double      nloops;

    if (!es->analyze || !planstate->instrument)
        return;

    if (which == 2)
        nfiltered = planstate->instrument->nfiltered2;
    else
        nfiltered = planstate->instrument->nfiltered1;
    nloops = planstate->instrument->nloops;

    /* In text mode, suppress zero counts; they're not interesting enough */
    if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
    {
        if (nloops > 0)
            ExplainPropertyFloat(qlabel, nfiltered / nloops, 0, es);
        else
            ExplainPropertyFloat(qlabel, 0.0, 0, es);
    }
}

static void show_merge_append_keys ( MergeAppendState mstate,
List ancestors,
ExplainState es 
) [static]

Definition at line 1689 of file explain.c.

References MergeAppend::numCols, PlanState::plan, MergeAppendState::ps, show_sort_keys_common(), and MergeAppend::sortColIdx.

Referenced by ExplainNode().

{
    MergeAppend *plan = (MergeAppend *) mstate->ps.plan;

    show_sort_keys_common((PlanState *) mstate,
                          plan->numCols, plan->sortColIdx,
                          ancestors, es);
}

static void show_modifytable_info ( ModifyTableState mtstate,
ExplainState es 
) [static]

Definition at line 2053 of file explain.c.

References FdwRoutine::ExplainForeignModify, ModifyTable::fdwPrivLists, linitial, NULL, PlanState::plan, ModifyTableState::ps, ModifyTableState::resultRelInfo, and ResultRelInfo::ri_FdwRoutine.

Referenced by ExplainNode().

{
    FdwRoutine *fdwroutine = mtstate->resultRelInfo->ri_FdwRoutine;

    /*
     * If the first target relation is a foreign table, call its FDW to
     * display whatever additional fields it wants to.  For now, we ignore the
     * possibility of other targets being foreign tables, although the API for
     * ExplainForeignModify is designed to allow them to be processed.
     */
    if (fdwroutine != NULL &&
        fdwroutine->ExplainForeignModify != NULL)
    {
        ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
        List       *fdw_private = (List *) linitial(node->fdwPrivLists);

        fdwroutine->ExplainForeignModify(mtstate,
                                         mtstate->resultRelInfo,
                                         fdw_private,
                                         0,
                                         es);
    }
}

static void show_plan_tlist ( PlanState planstate,
List ancestors,
ExplainState es 
) [static]

Definition at line 1558 of file explain.c.

References deparse_context_for_planstate(), deparse_expression(), ExplainPropertyList(), TargetEntry::expr, IsA, lappend(), lfirst, list_length(), NIL, PlanState::plan, ExplainState::rtable, ExplainState::rtable_names, and Plan::targetlist.

Referenced by ExplainNode().

{
    Plan       *plan = planstate->plan;
    List       *context;
    List       *result = NIL;
    bool        useprefix;
    ListCell   *lc;

    /* No work if empty tlist (this occurs eg in bitmap indexscans) */
    if (plan->targetlist == NIL)
        return;
    /* The tlist of an Append isn't real helpful, so suppress it */
    if (IsA(plan, Append))
        return;
    /* Likewise for MergeAppend and RecursiveUnion */
    if (IsA(plan, MergeAppend))
        return;
    if (IsA(plan, RecursiveUnion))
        return;

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

    /* Deparse each result column (we now include resjunk ones) */
    foreach(lc, plan->targetlist)
    {
        TargetEntry *tle = (TargetEntry *) lfirst(lc);

        result = lappend(result,
                         deparse_expression((Node *) tle->expr, context,
                                            useprefix, false));
    }

    /* Print results */
    ExplainPropertyList("Output", result, es);
}

static void show_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
bool  useprefix,
ExplainState es 
) [static]

Definition at line 1627 of file explain.c.

References make_ands_explicit(), NIL, and show_expression().

Referenced by show_scan_qual(), and show_upper_qual().

{
    Node       *node;

    /* No work if empty qual */
    if (qual == NIL)
        return;

    /* Convert AND list to explicit AND */
    node = (Node *) make_ands_explicit(qual);

    /* And show it */
    show_expression(node, qlabel, planstate, ancestors, useprefix, es);
}

static void show_scan_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
ExplainState es 
) [static]

Definition at line 1648 of file explain.c.

References IsA, PlanState::plan, show_qual(), and ExplainState::verbose.

Referenced by ExplainNode().

{
    bool        useprefix;

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

static void show_sort_info ( SortState sortstate,
ExplainState es 
) [static]

Definition at line 1742 of file explain.c.

References ExplainState::analyze, appendStringInfo(), appendStringInfoSpaces(), Assert, EXPLAIN_FORMAT_TEXT, ExplainPropertyLong(), ExplainPropertyText(), ExplainState::format, ExplainState::indent, IsA, NULL, SortState::sort_Done, ExplainState::str, tuplesort_get_stats(), and SortState::tuplesortstate.

Referenced by ExplainNode().

{
    Assert(IsA(sortstate, SortState));
    if (es->analyze && sortstate->sort_Done &&
        sortstate->tuplesortstate != NULL)
    {
        Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
        const char *sortMethod;
        const char *spaceType;
        long        spaceUsed;

        tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);

        if (es->format == EXPLAIN_FORMAT_TEXT)
        {
            appendStringInfoSpaces(es->str, es->indent * 2);
            appendStringInfo(es->str, "Sort Method: %s  %s: %ldkB\n",
                             sortMethod, spaceType, spaceUsed);
        }
        else
        {
            ExplainPropertyText("Sort Method", sortMethod, es);
            ExplainPropertyLong("Sort Space Used", spaceUsed, es);
            ExplainPropertyText("Sort Space Type", spaceType, es);
        }
    }
}

static void show_sort_keys ( SortState sortstate,
List ancestors,
ExplainState es 
) [static]

Definition at line 1676 of file explain.c.

References Sort::numCols, PlanState::plan, ScanState::ps, show_sort_keys_common(), Sort::sortColIdx, and SortState::ss.

Referenced by ExplainNode().

{
    Sort       *plan = (Sort *) sortstate->ss.ps.plan;

    show_sort_keys_common((PlanState *) sortstate,
                          plan->numCols, plan->sortColIdx,
                          ancestors, es);
}

static void show_sort_keys_common ( PlanState planstate,
int  nkeys,
AttrNumber keycols,
List ancestors,
ExplainState es 
) [static]

Definition at line 1700 of file explain.c.

References deparse_context_for_planstate(), deparse_expression(), elog, ERROR, ExplainPropertyList(), TargetEntry::expr, get_tle_by_resno(), lappend(), list_length(), PlanState::plan, ExplainState::rtable, ExplainState::rtable_names, Plan::targetlist, and ExplainState::verbose.

Referenced by show_merge_append_keys(), and show_sort_keys().

{
    Plan       *plan = planstate->plan;
    List       *context;
    List       *result = NIL;
    bool        useprefix;
    int         keyno;
    char       *exprstr;

    if (nkeys <= 0)
        return;

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

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

        if (!target)
            elog(ERROR, "no tlist entry for key %d", keyresno);
        /* Deparse the expression, showing any top-level cast */
        exprstr = deparse_expression((Node *) target->expr, context,
                                     useprefix, true);
        result = lappend(result, exprstr);
    }

    ExplainPropertyList("Sort Key", result, es);
}

static void show_upper_qual ( List qual,
const char *  qlabel,
PlanState planstate,
List ancestors,
ExplainState es 
) [static]

Definition at line 1662 of file explain.c.

References list_length(), ExplainState::rtable, show_qual(), and ExplainState::verbose.

Referenced by ExplainNode().

{
    bool        useprefix;

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


Variable Documentation

Definition at line 40 of file explain.c.

Referenced by explain_get_index_name().

Definition at line 37 of file explain.c.

Referenced by ExplainOneQuery().