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);