diff -cprN head/contrib/auto_explain/auto_explain.c work/contrib/auto_explain/auto_explain.c *** head/contrib/auto_explain/auto_explain.c Mon Aug 10 14:46:49 2009 --- work/contrib/auto_explain/auto_explain.c Mon Oct 5 11:50:46 2009 *************** PG_MODULE_MAGIC; *** 22,27 **** --- 22,28 ---- static int auto_explain_log_min_duration = -1; /* msec or -1 */ static bool auto_explain_log_analyze = false; static bool auto_explain_log_verbose = false; + static bool auto_explain_log_buffers = false; static int auto_explain_log_format = EXPLAIN_FORMAT_TEXT; static bool auto_explain_log_nested_statements = false; *************** _PG_init(void) *** 92,97 **** --- 93,108 ---- NULL, NULL); + DefineCustomBoolVariable("auto_explain.log_buffers", + "Log buffers usage.", + NULL, + &auto_explain_log_buffers, + false, + PGC_SUSET, + 0, + NULL, + NULL); + DefineCustomEnumVariable("auto_explain.log_format", "EXPLAIN format to be used for plan logging.", NULL, *************** explain_ExecutorEnd(QueryDesc *queryDesc *** 220,225 **** --- 231,237 ---- ExplainInitState(&es); es.analyze = (queryDesc->doInstrument && auto_explain_log_analyze); es.verbose = auto_explain_log_verbose; + es.buffers = (es.analyze && auto_explain_log_buffers); es.format = auto_explain_log_format; ExplainPrintPlan(&es, queryDesc); diff -cprN head/contrib/pg_stat_statements/pg_stat_statements.c work/contrib/pg_stat_statements/pg_stat_statements.c *** head/contrib/pg_stat_statements/pg_stat_statements.c Mon Jul 27 13:09:55 2009 --- work/contrib/pg_stat_statements/pg_stat_statements.c Mon Oct 5 14:13:16 2009 *************** *** 26,31 **** --- 26,32 ---- #include "catalog/pg_type.h" #include "executor/executor.h" #include "executor/instrument.h" + #include "funcapi.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "pgstat.h" *************** PG_MODULE_MAGIC; *** 43,49 **** #define PGSS_DUMP_FILE "global/pg_stat_statements.stat" /* This constant defines the magic number in the stats file header */ ! static const uint32 PGSS_FILE_HEADER = 0x20081202; /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ #define USAGE_EXEC(duration) (1.0) --- 44,50 ---- #define PGSS_DUMP_FILE "global/pg_stat_statements.stat" /* This constant defines the magic number in the stats file header */ ! static const uint32 PGSS_FILE_HEADER = 0x20090928; /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ #define USAGE_EXEC(duration) (1.0) *************** typedef struct Counters *** 77,82 **** --- 78,86 ---- int64 calls; /* # of times executed */ double total_time; /* total execution time in seconds */ int64 rows; /* total # of retrieved or affected rows */ + int64 blks_hit; /* total # of buffer hits */ + int64 blks_read; /* total # of disk blocks read */ + int64 temp_blks_read; /* total # of temp blocks read */ double usage; /* usage factor */ } Counters; *************** pgss_store(const char *query, const Inst *** 633,638 **** --- 637,645 ---- e->counters.calls += 1; e->counters.total_time += instr->total; e->counters.rows += rows; + e->counters.blks_hit += instr->blks_hit; + e->counters.blks_read += instr->blks_read; + e->counters.temp_blks_read += instr->temp_blks_read; e->counters.usage += usage; SpinLockRelease(&e->mutex); } *************** pg_stat_statements_reset(PG_FUNCTION_ARG *** 654,660 **** PG_RETURN_VOID(); } ! #define PG_STAT_STATEMENTS_COLS 6 /* * Retrieve statement statistics. --- 661,667 ---- PG_RETURN_VOID(); } ! #define PG_STAT_STATEMENTS_COLS 9 /* * Retrieve statement statistics. *************** pg_stat_statements(PG_FUNCTION_ARGS) *** 688,709 **** errmsg("materialize mode required, but it is not " \ "allowed in this context"))); per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); ! tupdesc = CreateTemplateTupleDesc(PG_STAT_STATEMENTS_COLS, false); ! TupleDescInitEntry(tupdesc, (AttrNumber) 1, "userid", ! OIDOID, -1, 0); ! TupleDescInitEntry(tupdesc, (AttrNumber) 2, "dbid", ! OIDOID, -1, 0); ! TupleDescInitEntry(tupdesc, (AttrNumber) 3, "query", ! TEXTOID, -1, 0); ! TupleDescInitEntry(tupdesc, (AttrNumber) 4, "calls", ! INT8OID, -1, 0); ! TupleDescInitEntry(tupdesc, (AttrNumber) 5, "total_time", ! FLOAT8OID, -1, 0); ! TupleDescInitEntry(tupdesc, (AttrNumber) 6, "rows", ! INT8OID, -1, 0); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; --- 695,708 ---- errmsg("materialize mode required, but it is not " \ "allowed in this context"))); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); ! tupdesc = CreateTupleDescCopy(tupdesc); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; *************** pg_stat_statements(PG_FUNCTION_ARGS) *** 757,762 **** --- 756,764 ---- values[i++] = Int64GetDatumFast(tmp.calls); values[i++] = Float8GetDatumFast(tmp.total_time); values[i++] = Int64GetDatumFast(tmp.rows); + values[i++] = Int64GetDatumFast(tmp.blks_hit); + values[i++] = Int64GetDatumFast(tmp.blks_read); + values[i++] = Int64GetDatumFast(tmp.temp_blks_read); Assert(i == PG_STAT_STATEMENTS_COLS); diff -cprN head/contrib/pg_stat_statements/pg_stat_statements.sql.in work/contrib/pg_stat_statements/pg_stat_statements.sql.in *** head/contrib/pg_stat_statements/pg_stat_statements.sql.in Mon Jan 5 07:19:59 2009 --- work/contrib/pg_stat_statements/pg_stat_statements.sql.in Mon Oct 5 14:12:51 2009 *************** CREATE FUNCTION pg_stat_statements( *** 15,21 **** OUT query text, OUT calls int8, OUT total_time float8, ! OUT rows int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' --- 15,24 ---- OUT query text, OUT calls int8, OUT total_time float8, ! OUT rows int8, ! OUT blks_hit int8, ! OUT blks_read int8, ! OUT temp_blks_read int8 ) RETURNS SETOF record AS 'MODULE_PATHNAME' diff -cprN head/doc/src/sgml/auto-explain.sgml work/doc/src/sgml/auto-explain.sgml *** head/doc/src/sgml/auto-explain.sgml Mon Aug 10 14:46:50 2009 --- work/doc/src/sgml/auto-explain.sgml Mon Oct 5 11:50:46 2009 *************** LOAD 'auto_explain'; *** 104,109 **** --- 104,128 ---- + auto_explain.log_buffers (boolean) + + + auto_explain.log_buffers configuration parameter + + + + auto_explain.log_buffers causes EXPLAIN + (ANALYZE, BUFFERS) output, rather than just EXPLAIN + output, to be printed when an execution plan is logged. This parameter is + off by default. Only superusers can change this setting. Also, this + parameter only has effect if auto_explain.log_analyze + parameter is set. + + + + + + auto_explain.log_format (enum) diff -cprN head/doc/src/sgml/pgstatstatements.sgml work/doc/src/sgml/pgstatstatements.sgml *** head/doc/src/sgml/pgstatstatements.sgml Mon May 18 20:08:24 2009 --- work/doc/src/sgml/pgstatstatements.sgml Mon Oct 5 14:12:51 2009 *************** *** 85,90 **** --- 85,111 ---- Total number of rows retrieved or affected by the statement + + blks_hit + bigint + + Total number of buffer hits by the statement + + + + blks_read + bigint + + Total number of disk blocks read by the statement + + + + temp_blks_read + bigint + + Total number of temp blocks read by the statement + + diff -cprN head/doc/src/sgml/ref/explain.sgml work/doc/src/sgml/ref/explain.sgml *** head/doc/src/sgml/ref/explain.sgml Mon Aug 10 14:46:50 2009 --- work/doc/src/sgml/ref/explain.sgml Mon Oct 5 13:11:22 2009 *************** PostgreSQL documentation *** 31,37 **** ! EXPLAIN [ ( { ANALYZE boolean | VERBOSE boolean | COSTS boolean | FORMAT { TEXT | XML | JSON } } [, ...] ) ] statement EXPLAIN [ ANALYZE ] [ VERBOSE ] statement --- 31,37 ---- ! EXPLAIN [ ( { ANALYZE boolean | VERBOSE boolean | COSTS boolean | BUFFERS boolean | FORMAT { TEXT | XML | JSON } } [, ...] ) ] statement EXPLAIN [ ANALYZE ] [ VERBOSE ] statement *************** ROLLBACK; *** 140,145 **** --- 140,157 ---- + BUFFERS + + + Include information on the buffers. Specifically, include the number of + buffer hits, number of disk blocks read, and number of local buffer read. + This parameter should be used with ANALYZE parameter. + Also, this parameter defaults to FALSE. + + + + + FORMAT diff -cprN head/src/backend/commands/explain.c work/src/backend/commands/explain.c *** head/src/backend/commands/explain.c Sat Aug 22 11:06:32 2009 --- work/src/backend/commands/explain.c Mon Oct 5 14:16:02 2009 *************** ExplainQuery(ExplainStmt *stmt, const ch *** 127,132 **** --- 127,134 ---- 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, "format") == 0) { char *p = defGetString(opt); *************** ExplainQuery(ExplainStmt *stmt, const ch *** 150,155 **** --- 152,162 ---- opt->defname))); } + if (es.buffers && !es.analyze) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("EXPLAIN option BUFFERS requires ANALYZE"))); + /* Convert parameter type data to the form parser wants */ getParamListTypes(params, ¶m_types, &num_params); *************** ExplainNode(Plan *plan, PlanState *plans *** 1019,1024 **** --- 1026,1052 ---- break; } + /* Show buffer usage */ + if (es->buffers) + { + long num_hit = planstate->instrument->blks_hit; + long num_read = planstate->instrument->blks_read; + long num_temp_read = planstate->instrument->temp_blks_read; + + if (es->format == EXPLAIN_FORMAT_TEXT) + { + appendStringInfoSpaces(es->str, es->indent * 2); + appendStringInfo(es->str, "Blocks Hit: %ld Read: %ld Temp Read: %ld\n", + num_hit, num_read, num_temp_read); + } + else + { + ExplainPropertyLong("Hit Blocks", num_hit, es); + ExplainPropertyLong("Read Blocks", num_read, es); + ExplainPropertyLong("Temp Read Blocks", num_temp_read, es); + } + } + /* Get ready to display the child plans */ haschildren = plan->initPlan || outerPlan(plan) || diff -cprN head/src/backend/executor/execMain.c work/src/backend/executor/execMain.c *** head/src/backend/executor/execMain.c Mon Sep 28 05:09:57 2009 --- work/src/backend/executor/execMain.c Mon Oct 5 14:12:52 2009 *************** standard_ExecutorRun(QueryDesc *queryDes *** 267,272 **** --- 267,273 ---- DestReceiver *dest; bool sendTuples; MemoryContext oldcontext; + Instrumentation *save_TopInstrument = NULL; /* sanity checks */ Assert(queryDesc != NULL); *************** standard_ExecutorRun(QueryDesc *queryDes *** 282,288 **** --- 283,293 ---- /* Allow instrumentation of ExecutorRun overall runtime */ if (queryDesc->totaltime) + { InstrStartNode(queryDesc->totaltime); + save_TopInstrument = TopInstrument; + TopInstrument = queryDesc->totaltime; + } /* * extract information from the query descriptor and the query feature. *************** standard_ExecutorRun(QueryDesc *queryDes *** 320,326 **** --- 325,340 ---- (*dest->rShutdown) (dest); if (queryDesc->totaltime) + { InstrStopNode(queryDesc->totaltime, estate->es_processed); + if (save_TopInstrument) + { + save_TopInstrument->blks_hit += queryDesc->totaltime->blks_hit; + save_TopInstrument->blks_read += queryDesc->totaltime->blks_read; + save_TopInstrument->temp_blks_read += queryDesc->totaltime->temp_blks_read; + } + TopInstrument = save_TopInstrument; + } MemoryContextSwitchTo(oldcontext); } diff -cprN head/src/backend/executor/instrument.c work/src/backend/executor/instrument.c *** head/src/backend/executor/instrument.c Fri Jan 2 02:23:41 2009 --- work/src/backend/executor/instrument.c Mon Oct 5 14:13:52 2009 *************** *** 16,22 **** --- 16,26 ---- #include #include "executor/instrument.h" + #include "storage/buf_internals.h" + #include "storage/bufmgr.h" + Instrumentation *CurrentInstrument = NULL; + Instrumentation *TopInstrument = NULL; /* Allocate new instrumentation structure(s) */ Instrumentation * *************** InstrStartNode(Instrumentation *instr) *** 37,42 **** --- 41,50 ---- INSTR_TIME_SET_CURRENT(instr->starttime); else elog(DEBUG2, "InstrStartNode called twice in a row"); + + /* push stack */ + instr->prev = CurrentInstrument; + CurrentInstrument = instr; } /* Exit from a plan node */ *************** void *** 44,49 **** --- 52,85 ---- InstrStopNode(Instrumentation *instr, double nTuples) { instr_time endtime; + long num_get; + long num_hit; + long num_read; + long num_temp_read; + + Assert(instr == CurrentInstrument); + + num_get = ReadBufferCount + ReadLocalBufferCount; + num_hit = BufferHitCount + LocalBufferHitCount; + num_read = num_get - num_hit; + num_temp_read = BufFileReadCount; + + /* count buffer usage per plan node */ + instr->blks_hit += num_hit; + instr->blks_read += num_read; + instr->temp_blks_read += num_temp_read; + + /* accumulate per-node buffer statistics into top node */ + if (TopInstrument && TopInstrument != CurrentInstrument) + { + TopInstrument->blks_hit += num_hit; + TopInstrument->blks_read += num_read; + TopInstrument->temp_blks_read += num_temp_read; + } + + /* reset buffer usage and pop stack */ + ResetLocalBufferUsage(); + CurrentInstrument = instr->prev; /* count the returned tuples */ instr->tuplecount += nTuples; diff -cprN head/src/backend/storage/buffer/bufmgr.c work/src/backend/storage/buffer/bufmgr.c *** head/src/backend/storage/buffer/bufmgr.c Thu Jun 11 23:49:01 2009 --- work/src/backend/storage/buffer/bufmgr.c Mon Oct 5 13:59:31 2009 *************** static bool IsForInput; *** 79,84 **** --- 79,94 ---- /* local state for LockBufferForCleanup */ static volatile BufferDesc *PinCountWaitBuf = NULL; + /* statistics counters for log_[parser|planner|executor|statement]_stats */ + static long GlobalReadBufferCount; + static long GlobalReadLocalBufferCount; + static long GlobalBufferHitCount; + static long GlobalLocalBufferHitCount; + static long GlobalBufferFlushCount; + static long GlobalLocalBufferFlushCount; + static long GlobalBufFileReadCount; + static long GlobalBufFileWriteCount; + static Buffer ReadBuffer_common(SMgrRelation reln, bool isLocalBuf, ForkNumber forkNum, BlockNumber blockNum, *************** ShowBufferUsage(void) *** 1620,1646 **** float hitrate; float localhitrate; initStringInfo(&str); ! if (ReadBufferCount == 0) hitrate = 0.0; else ! hitrate = (float) BufferHitCount *100.0 / ReadBufferCount; ! if (ReadLocalBufferCount == 0) localhitrate = 0.0; else ! localhitrate = (float) LocalBufferHitCount *100.0 / ReadLocalBufferCount; appendStringInfo(&str, "!\tShared blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ! ReadBufferCount - BufferHitCount, BufferFlushCount, hitrate); appendStringInfo(&str, "!\tLocal blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ! ReadLocalBufferCount - LocalBufferHitCount, LocalBufferFlushCount, localhitrate); appendStringInfo(&str, "!\tDirect blocks: %10ld read, %10ld written\n", ! BufFileReadCount, BufFileWriteCount); return str.data; } --- 1630,1658 ---- float hitrate; float localhitrate; + ResetLocalBufferUsage(); + initStringInfo(&str); ! if (GlobalReadBufferCount == 0) hitrate = 0.0; else ! hitrate = (float) GlobalBufferHitCount *100.0 / GlobalReadBufferCount; ! if (GlobalReadLocalBufferCount == 0) localhitrate = 0.0; else ! localhitrate = (float) GlobalLocalBufferHitCount *100.0 / GlobalReadLocalBufferCount; appendStringInfo(&str, "!\tShared blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ! GlobalReadBufferCount - GlobalBufferHitCount, GlobalBufferFlushCount, hitrate); appendStringInfo(&str, "!\tLocal blocks: %10ld read, %10ld written, buffer hit rate = %.2f%%\n", ! GlobalReadLocalBufferCount - GlobalLocalBufferHitCount, GlobalLocalBufferFlushCount, localhitrate); appendStringInfo(&str, "!\tDirect blocks: %10ld read, %10ld written\n", ! GlobalBufFileReadCount, GlobalBufFileWriteCount); return str.data; } *************** ResetBufferUsage(void) *** 1656,1661 **** --- 1668,1704 ---- LocalBufferFlushCount = 0; BufFileReadCount = 0; BufFileWriteCount = 0; + + GlobalBufferHitCount = 0; + GlobalReadBufferCount = 0; + GlobalBufferFlushCount = 0; + GlobalLocalBufferHitCount = 0; + GlobalReadLocalBufferCount = 0; + GlobalLocalBufferFlushCount = 0; + GlobalBufFileReadCount = 0; + GlobalBufFileWriteCount = 0; + } + + void + ResetLocalBufferUsage(void) + { + GlobalReadBufferCount += ReadBufferCount; + GlobalReadLocalBufferCount += ReadLocalBufferCount; + GlobalBufferHitCount += BufferHitCount; + GlobalLocalBufferHitCount += LocalBufferHitCount; + GlobalBufferFlushCount += BufferFlushCount; + GlobalLocalBufferFlushCount += LocalBufferFlushCount; + GlobalBufFileReadCount += BufFileReadCount; + GlobalBufFileWriteCount += BufFileWriteCount; + + BufferHitCount = 0; + ReadBufferCount = 0; + BufferFlushCount = 0; + LocalBufferHitCount = 0; + ReadLocalBufferCount = 0; + LocalBufferFlushCount = 0; + BufFileReadCount = 0; + BufFileWriteCount = 0; } /* diff -cprN head/src/backend/tcop/postgres.c work/src/backend/tcop/postgres.c *** head/src/backend/tcop/postgres.c Tue Sep 1 11:54:51 2009 --- work/src/backend/tcop/postgres.c Mon Oct 5 11:50:46 2009 *************** *** 44,49 **** --- 44,50 ---- #include "catalog/pg_type.h" #include "commands/async.h" #include "commands/prepare.h" + #include "executor/instrument.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" #include "libpq/pqsignal.h" *************** PostgresMain(int argc, char *argv[], con *** 3482,3487 **** --- 3483,3492 ---- */ doing_extended_query_message = false; + /* Reset buffer usage counters */ + CurrentInstrument = TopInstrument = NULL; + ResetLocalBufferUsage(); + /* * Release storage left over from prior query cycle, and create a new * query input buffer in the cleared MessageContext. diff -cprN head/src/include/commands/explain.h work/src/include/commands/explain.h *** head/src/include/commands/explain.h Mon Aug 10 14:46:50 2009 --- work/src/include/commands/explain.h Mon Oct 5 11:50:46 2009 *************** typedef struct ExplainState *** 29,34 **** --- 29,35 ---- bool verbose; /* be verbose */ bool analyze; /* print actual times */ bool costs; /* print costs */ + bool buffers; /* print buffer usage */ ExplainFormat format; /* output format */ /* other states */ PlannedStmt *pstmt; /* top of plan */ diff -cprN head/src/include/executor/instrument.h work/src/include/executor/instrument.h *** head/src/include/executor/instrument.h Fri Jan 2 02:23:59 2009 --- work/src/include/executor/instrument.h Mon Oct 5 14:14:25 2009 *************** typedef struct Instrumentation *** 29,36 **** --- 29,45 ---- double total; /* Total total time (in seconds) */ double ntuples; /* Total tuples produced */ double nloops; /* # of run cycles for this node */ + /* Buffer usage */ + long blks_hit; /* # of buffer hits */ + long blks_read; /* # of disk blocks read */ + long temp_blks_read; /* # of temp blocks read */ + /* previous node in stack */ + struct Instrumentation *prev; } Instrumentation; + extern Instrumentation *CurrentInstrument; + extern Instrumentation *TopInstrument; + extern Instrumentation *InstrAlloc(int n); extern void InstrStartNode(Instrumentation *instr); extern void InstrStopNode(Instrumentation *instr, double nTuples); diff -cprN head/src/include/storage/bufmgr.h work/src/include/storage/bufmgr.h *** head/src/include/storage/bufmgr.h Thu Jun 11 23:49:12 2009 --- work/src/include/storage/bufmgr.h Mon Oct 5 11:50:46 2009 *************** extern void InitBufferPoolAccess(void); *** 175,180 **** --- 175,181 ---- extern void InitBufferPoolBackend(void); extern char *ShowBufferUsage(void); extern void ResetBufferUsage(void); + extern void ResetLocalBufferUsage(void); extern void AtEOXact_Buffers(bool isCommit); extern void PrintBufferLeakWarning(Buffer buffer); extern void CheckPointBuffers(int flags);