Header And Logo

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

auto_explain.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * auto_explain.c
00004  *
00005  *
00006  * Copyright (c) 2008-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *    contrib/auto_explain/auto_explain.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 #include "postgres.h"
00014 
00015 #include <limits.h>
00016 
00017 #include "commands/explain.h"
00018 #include "executor/instrument.h"
00019 #include "utils/guc.h"
00020 
00021 PG_MODULE_MAGIC;
00022 
00023 /* GUC variables */
00024 static int  auto_explain_log_min_duration = -1; /* msec or -1 */
00025 static bool auto_explain_log_analyze = false;
00026 static bool auto_explain_log_verbose = false;
00027 static bool auto_explain_log_buffers = false;
00028 static bool auto_explain_log_timing = false;
00029 static int  auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
00030 static bool auto_explain_log_nested_statements = false;
00031 
00032 static const struct config_enum_entry format_options[] = {
00033     {"text", EXPLAIN_FORMAT_TEXT, false},
00034     {"xml", EXPLAIN_FORMAT_XML, false},
00035     {"json", EXPLAIN_FORMAT_JSON, false},
00036     {"yaml", EXPLAIN_FORMAT_YAML, false},
00037     {NULL, 0, false}
00038 };
00039 
00040 /* Current nesting depth of ExecutorRun calls */
00041 static int  nesting_level = 0;
00042 
00043 /* Saved hook values in case of unload */
00044 static ExecutorStart_hook_type prev_ExecutorStart = NULL;
00045 static ExecutorRun_hook_type prev_ExecutorRun = NULL;
00046 static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
00047 static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
00048 
00049 #define auto_explain_enabled() \
00050     (auto_explain_log_min_duration >= 0 && \
00051      (nesting_level == 0 || auto_explain_log_nested_statements))
00052 
00053 void        _PG_init(void);
00054 void        _PG_fini(void);
00055 
00056 static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
00057 static void explain_ExecutorRun(QueryDesc *queryDesc,
00058                     ScanDirection direction,
00059                     long count);
00060 static void explain_ExecutorFinish(QueryDesc *queryDesc);
00061 static void explain_ExecutorEnd(QueryDesc *queryDesc);
00062 
00063 
00064 /*
00065  * Module load callback
00066  */
00067 void
00068 _PG_init(void)
00069 {
00070     /* Define custom GUC variables. */
00071     DefineCustomIntVariable("auto_explain.log_min_duration",
00072          "Sets the minimum execution time above which plans will be logged.",
00073                          "Zero prints all plans. -1 turns this feature off.",
00074                             &auto_explain_log_min_duration,
00075                             -1,
00076                             -1, INT_MAX / 1000,
00077                             PGC_SUSET,
00078                             GUC_UNIT_MS,
00079                             NULL,
00080                             NULL,
00081                             NULL);
00082 
00083     DefineCustomBoolVariable("auto_explain.log_analyze",
00084                              "Use EXPLAIN ANALYZE for plan logging.",
00085                              NULL,
00086                              &auto_explain_log_analyze,
00087                              false,
00088                              PGC_SUSET,
00089                              0,
00090                              NULL,
00091                              NULL,
00092                              NULL);
00093 
00094     DefineCustomBoolVariable("auto_explain.log_verbose",
00095                              "Use EXPLAIN VERBOSE for plan logging.",
00096                              NULL,
00097                              &auto_explain_log_verbose,
00098                              false,
00099                              PGC_SUSET,
00100                              0,
00101                              NULL,
00102                              NULL,
00103                              NULL);
00104 
00105     DefineCustomBoolVariable("auto_explain.log_buffers",
00106                              "Log buffers usage.",
00107                              NULL,
00108                              &auto_explain_log_buffers,
00109                              false,
00110                              PGC_SUSET,
00111                              0,
00112                              NULL,
00113                              NULL,
00114                              NULL);
00115 
00116     DefineCustomEnumVariable("auto_explain.log_format",
00117                              "EXPLAIN format to be used for plan logging.",
00118                              NULL,
00119                              &auto_explain_log_format,
00120                              EXPLAIN_FORMAT_TEXT,
00121                              format_options,
00122                              PGC_SUSET,
00123                              0,
00124                              NULL,
00125                              NULL,
00126                              NULL);
00127 
00128     DefineCustomBoolVariable("auto_explain.log_nested_statements",
00129                              "Log nested statements.",
00130                              NULL,
00131                              &auto_explain_log_nested_statements,
00132                              false,
00133                              PGC_SUSET,
00134                              0,
00135                              NULL,
00136                              NULL,
00137                              NULL);
00138 
00139     DefineCustomBoolVariable("auto_explain.log_timing",
00140                              "Collect timing data, not just row counts.",
00141                              NULL,
00142                              &auto_explain_log_timing,
00143                              true,
00144                              PGC_SUSET,
00145                              0,
00146                              NULL,
00147                              NULL,
00148                              NULL);
00149 
00150     EmitWarningsOnPlaceholders("auto_explain");
00151 
00152     /* Install hooks. */
00153     prev_ExecutorStart = ExecutorStart_hook;
00154     ExecutorStart_hook = explain_ExecutorStart;
00155     prev_ExecutorRun = ExecutorRun_hook;
00156     ExecutorRun_hook = explain_ExecutorRun;
00157     prev_ExecutorFinish = ExecutorFinish_hook;
00158     ExecutorFinish_hook = explain_ExecutorFinish;
00159     prev_ExecutorEnd = ExecutorEnd_hook;
00160     ExecutorEnd_hook = explain_ExecutorEnd;
00161 }
00162 
00163 /*
00164  * Module unload callback
00165  */
00166 void
00167 _PG_fini(void)
00168 {
00169     /* Uninstall hooks. */
00170     ExecutorStart_hook = prev_ExecutorStart;
00171     ExecutorRun_hook = prev_ExecutorRun;
00172     ExecutorFinish_hook = prev_ExecutorFinish;
00173     ExecutorEnd_hook = prev_ExecutorEnd;
00174 }
00175 
00176 /*
00177  * ExecutorStart hook: start up logging if needed
00178  */
00179 static void
00180 explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
00181 {
00182     if (auto_explain_enabled())
00183     {
00184         /* Enable per-node instrumentation iff log_analyze is required. */
00185         if (auto_explain_log_analyze && (eflags & EXEC_FLAG_EXPLAIN_ONLY) == 0)
00186         {
00187             if (auto_explain_log_timing)
00188                 queryDesc->instrument_options |= INSTRUMENT_TIMER;
00189             else
00190                 queryDesc->instrument_options |= INSTRUMENT_ROWS;
00191 
00192 
00193             if (auto_explain_log_buffers)
00194                 queryDesc->instrument_options |= INSTRUMENT_BUFFERS;
00195         }
00196     }
00197 
00198     if (prev_ExecutorStart)
00199         prev_ExecutorStart(queryDesc, eflags);
00200     else
00201         standard_ExecutorStart(queryDesc, eflags);
00202 
00203     if (auto_explain_enabled())
00204     {
00205         /*
00206          * Set up to track total elapsed time in ExecutorRun.  Make sure the
00207          * space is allocated in the per-query context so it will go away at
00208          * ExecutorEnd.
00209          */
00210         if (queryDesc->totaltime == NULL)
00211         {
00212             MemoryContext oldcxt;
00213 
00214             oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
00215             queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
00216             MemoryContextSwitchTo(oldcxt);
00217         }
00218     }
00219 }
00220 
00221 /*
00222  * ExecutorRun hook: all we need do is track nesting depth
00223  */
00224 static void
00225 explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
00226 {
00227     nesting_level++;
00228     PG_TRY();
00229     {
00230         if (prev_ExecutorRun)
00231             prev_ExecutorRun(queryDesc, direction, count);
00232         else
00233             standard_ExecutorRun(queryDesc, direction, count);
00234         nesting_level--;
00235     }
00236     PG_CATCH();
00237     {
00238         nesting_level--;
00239         PG_RE_THROW();
00240     }
00241     PG_END_TRY();
00242 }
00243 
00244 /*
00245  * ExecutorFinish hook: all we need do is track nesting depth
00246  */
00247 static void
00248 explain_ExecutorFinish(QueryDesc *queryDesc)
00249 {
00250     nesting_level++;
00251     PG_TRY();
00252     {
00253         if (prev_ExecutorFinish)
00254             prev_ExecutorFinish(queryDesc);
00255         else
00256             standard_ExecutorFinish(queryDesc);
00257         nesting_level--;
00258     }
00259     PG_CATCH();
00260     {
00261         nesting_level--;
00262         PG_RE_THROW();
00263     }
00264     PG_END_TRY();
00265 }
00266 
00267 /*
00268  * ExecutorEnd hook: log results if needed
00269  */
00270 static void
00271 explain_ExecutorEnd(QueryDesc *queryDesc)
00272 {
00273     if (queryDesc->totaltime && auto_explain_enabled())
00274     {
00275         double      msec;
00276 
00277         /*
00278          * Make sure stats accumulation is done.  (Note: it's okay if several
00279          * levels of hook all do this.)
00280          */
00281         InstrEndLoop(queryDesc->totaltime);
00282 
00283         /* Log plan if duration is exceeded. */
00284         msec = queryDesc->totaltime->total * 1000.0;
00285         if (msec >= auto_explain_log_min_duration)
00286         {
00287             ExplainState es;
00288 
00289             ExplainInitState(&es);
00290             es.analyze = (queryDesc->instrument_options && auto_explain_log_analyze);
00291             es.verbose = auto_explain_log_verbose;
00292             es.buffers = (es.analyze && auto_explain_log_buffers);
00293             es.format = auto_explain_log_format;
00294 
00295             ExplainBeginOutput(&es);
00296             ExplainQueryText(&es, queryDesc);
00297             ExplainPrintPlan(&es, queryDesc);
00298             ExplainEndOutput(&es);
00299 
00300             /* Remove last line break */
00301             if (es.str->len > 0 && es.str->data[es.str->len - 1] == '\n')
00302                 es.str->data[--es.str->len] = '\0';
00303 
00304             /* Fix JSON to output an object */
00305             if (auto_explain_log_format == EXPLAIN_FORMAT_JSON)
00306             {
00307                 es.str->data[0] = '{';
00308                 es.str->data[es.str->len - 1] = '}';
00309             }
00310 
00311             /*
00312              * Note: we rely on the existing logging of context or
00313              * debug_query_string to identify just which statement is being
00314              * reported.  This isn't ideal but trying to do it here would
00315              * often result in duplication.
00316              */
00317             ereport(LOG,
00318                     (errmsg("duration: %.3f ms  plan:\n%s",
00319                             msec, es.str->data),
00320                      errhidestmt(true)));
00321 
00322             pfree(es.str->data);
00323         }
00324     }
00325 
00326     if (prev_ExecutorEnd)
00327         prev_ExecutorEnd(queryDesc);
00328     else
00329         standard_ExecutorEnd(queryDesc);
00330 }