]> granicus.if.org Git - postgresql/commitdiff
Make EXPLAIN (BUFFERS) track blocks dirtied, as well as those written.
authorRobert Haas <rhaas@postgresql.org>
Thu, 23 Feb 2012 01:33:05 +0000 (20:33 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 23 Feb 2012 01:33:05 +0000 (20:33 -0500)
Also expose the new counters through pg_stat_statements.

Patch by me.  Review by Fujii Masao and Greg Smith.

12 files changed:
contrib/pg_stat_statements/Makefile
contrib/pg_stat_statements/pg_stat_statements--1.0--1.1.sql [new file with mode: 0644]
contrib/pg_stat_statements/pg_stat_statements--1.1.sql [new file with mode: 0644]
contrib/pg_stat_statements/pg_stat_statements.c
contrib/pg_stat_statements/pg_stat_statements.control
doc/src/sgml/pgstatstatements.sgml
doc/src/sgml/ref/explain.sgml
src/backend/commands/explain.c
src/backend/executor/instrument.c
src/backend/storage/buffer/bufmgr.c
src/backend/storage/buffer/localbuf.c
src/include/executor/instrument.h

index e086fd8a827dd22dff806a0009637fb68b505a69..e8aed6121640248d962bc3a7bb8e512df6ffaa65 100644 (file)
@@ -4,7 +4,8 @@ MODULE_big = pg_stat_statements
 OBJS = pg_stat_statements.o
 
 EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.0.sql pg_stat_statements--unpackaged--1.0.sql
+DATA = pg_stat_statements--1.1.sql pg_stat_statements--1.0--1.1.sql \
+       pg_stat_statements--unpackaged--1.0.sql
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.0--1.1.sql b/contrib/pg_stat_statements/pg_stat_statements--1.0--1.1.sql
new file mode 100644 (file)
index 0000000..223271d
--- /dev/null
@@ -0,0 +1,40 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE" to load this file. \quit
+
+/* First we have to remove them from the extension */
+ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements;
+ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements();
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements();
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(
+    OUT userid oid,
+    OUT dbid oid,
+    OUT query text,
+    OUT calls int8,
+    OUT total_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements();
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.1.sql b/contrib/pg_stat_statements/pg_stat_statements--1.1.sql
new file mode 100644 (file)
index 0000000..1233736
--- /dev/null
@@ -0,0 +1,41 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pg_stat_statements" to load this file. \quit
+
+-- Register functions.
+CREATE FUNCTION pg_stat_statements_reset()
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+CREATE FUNCTION pg_stat_statements(
+    OUT userid oid,
+    OUT dbid oid,
+    OUT query text,
+    OUT calls int8,
+    OUT total_time float8,
+    OUT rows int8,
+    OUT shared_blks_hit int8,
+    OUT shared_blks_read int8,
+    OUT shared_blks_dirtied int8,
+    OUT shared_blks_written int8,
+    OUT local_blks_hit int8,
+    OUT local_blks_read int8,
+    OUT local_blks_dirtied int8,
+    OUT local_blks_written int8,
+    OUT temp_blks_read int8,
+    OUT temp_blks_written int8
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME'
+LANGUAGE C;
+
+-- Register a view on the function for ease of use.
+CREATE VIEW pg_stat_statements AS
+  SELECT * FROM pg_stat_statements();
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
+
+-- Don't want this to be available to non-superusers.
+REVOKE ALL ON FUNCTION pg_stat_statements_reset() FROM PUBLIC;
index 434aa71a33cd69b6f8e53d54d1c7741e11bbadbc..914fbf270d7db4e13cad18e7fba1901c2df27618 100644 (file)
@@ -77,9 +77,11 @@ typedef struct Counters
        int64           rows;                   /* total # of retrieved or affected rows */
        int64           shared_blks_hit;        /* # of shared buffer hits */
        int64           shared_blks_read;               /* # of shared disk blocks read */
+       int64           shared_blks_dirtied;    /* # of shared disk blocks dirtied */
        int64           shared_blks_written;    /* # of shared disk blocks written */
        int64           local_blks_hit; /* # of local buffer hits */
        int64           local_blks_read;        /* # of local disk blocks read */
+       int64           local_blks_dirtied;             /* # of local disk blocks dirtied */
        int64           local_blks_written;             /* # of local disk blocks written */
        int64           temp_blks_read; /* # of temp blocks read */
        int64           temp_blks_written;              /* # of temp blocks written */
@@ -652,12 +654,16 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
                        pgBufferUsage.shared_blks_hit - bufusage.shared_blks_hit;
                bufusage.shared_blks_read =
                        pgBufferUsage.shared_blks_read - bufusage.shared_blks_read;
+               bufusage.shared_blks_dirtied =
+                       pgBufferUsage.shared_blks_dirtied - bufusage.shared_blks_dirtied;
                bufusage.shared_blks_written =
                        pgBufferUsage.shared_blks_written - bufusage.shared_blks_written;
                bufusage.local_blks_hit =
                        pgBufferUsage.local_blks_hit - bufusage.local_blks_hit;
                bufusage.local_blks_read =
                        pgBufferUsage.local_blks_read - bufusage.local_blks_read;
+               bufusage.local_blks_dirtied =
+                       pgBufferUsage.local_blks_dirtied - bufusage.local_blks_dirtied;
                bufusage.local_blks_written =
                        pgBufferUsage.local_blks_written - bufusage.local_blks_written;
                bufusage.temp_blks_read =
@@ -766,9 +772,11 @@ pgss_store(const char *query, double total_time, uint64 rows,
                e->counters.rows += rows;
                e->counters.shared_blks_hit += bufusage->shared_blks_hit;
                e->counters.shared_blks_read += bufusage->shared_blks_read;
+               e->counters.shared_blks_dirtied += bufusage->shared_blks_dirtied;
                e->counters.shared_blks_written += bufusage->shared_blks_written;
                e->counters.local_blks_hit += bufusage->local_blks_hit;
                e->counters.local_blks_read += bufusage->local_blks_read;
+               e->counters.local_blks_dirtied += bufusage->local_blks_dirtied;
                e->counters.local_blks_written += bufusage->local_blks_written;
                e->counters.temp_blks_read += bufusage->temp_blks_read;
                e->counters.temp_blks_written += bufusage->temp_blks_written;
@@ -793,7 +801,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
        PG_RETURN_VOID();
 }
 
-#define PG_STAT_STATEMENTS_COLS                14
+#define PG_STAT_STATEMENTS_COLS_V1_0   14
+#define PG_STAT_STATEMENTS_COLS                        16
 
 /*
  * Retrieve statement statistics.
@@ -810,6 +819,7 @@ pg_stat_statements(PG_FUNCTION_ARGS)
        bool            is_superuser = superuser();
        HASH_SEQ_STATUS hash_seq;
        pgssEntry  *entry;
+       bool            sql_supports_dirty_counters = true;
 
        if (!pgss || !pgss_hash)
                ereport(ERROR,
@@ -830,6 +840,8 @@ pg_stat_statements(PG_FUNCTION_ARGS)
        /* 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");
+       if (tupdesc->natts == PG_STAT_STATEMENTS_COLS_V1_0)
+               sql_supports_dirty_counters = false;
 
        per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
        oldcontext = MemoryContextSwitchTo(per_query_ctx);
@@ -887,14 +899,19 @@ pg_stat_statements(PG_FUNCTION_ARGS)
                values[i++] = Int64GetDatumFast(tmp.rows);
                values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
                values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
+               if (sql_supports_dirty_counters)
+                       values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied);
                values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
                values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
                values[i++] = Int64GetDatumFast(tmp.local_blks_read);
+               if (sql_supports_dirty_counters)
+                       values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied);
                values[i++] = Int64GetDatumFast(tmp.local_blks_written);
                values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
                values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
 
-               Assert(i == PG_STAT_STATEMENTS_COLS);
+               Assert(i == sql_supports_dirty_counters ? \
+                       PG_STAT_STATEMENTS_COLS : PG_STAT_STATEMENTS_COLS_V1_0);
 
                tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
index 6f9a9471228df72dd9669dff346bb0ed636bc2b2..428fbb23749ad351656eee9d367c92862a6e1361 100644 (file)
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track execution statistics of all SQL statements executed'
-default_version = '1.0'
+default_version = '1.1'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
index 5a0230c428618b53069f6b2f38e2d4acdcdaebff..ab34ca193a4edb28c2e9ff15690fed587c0b34ff 100644 (file)
       <entry>Total number of shared blocks reads by the statement</entry>
      </row>
 
+     <row>
+      <entry><structfield>shared_blks_dirtied</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry></entry>
+      <entry>Total number of shared blocks dirtied by the statement</entry>
+     </row>
+
      <row>
       <entry><structfield>shared_blks_written</structfield></entry>
       <entry><type>bigint</type></entry>
       <entry>Total number of local blocks reads by the statement</entry>
      </row>
 
+     <row>
+      <entry><structfield>local_blks_dirtied</structfield></entry>
+      <entry><type>bigint</type></entry>
+      <entry></entry>
+      <entry>Total number of local blocks dirtied by the statement</entry>
+     </row>
+
      <row>
       <entry><structfield>local_blks_written</structfield></entry>
       <entry><type>bigint</type></entry>
index 419b72cad348fe8d2e522a78eee981a121e8c402..1f35a1d155279f4f50aefd4b32739d4e4259a3b1 100644 (file)
@@ -155,14 +155,20 @@ ROLLBACK;
     <listitem>
      <para>
       Include information on buffer usage. Specifically, include the number of
-      shared blocks hits, reads, and writes, the number of local blocks hits,
-      reads, and writes, and the number of temp blocks reads and writes.
-      A <quote>hit</> means that a read was avoided because the block was
+      shared blocks hit, read, dirtied, and written, the number of local blocks
+      hit, read, dirtied, and written, and the number of temp blocks read and
+      written.
+      A <emphasis>hit</> means that a read was avoided because the block was
       found already in cache when needed.
       Shared blocks contain data from regular tables and indexes;
       local blocks contain data from temporary tables and indexes;
       while temp blocks contain short-term working data used in sorts, hashes,
       Materialize plan nodes, and similar cases.
+      The number of blocks <emphasis>dirtied</> indicates the number of
+      previously unmodified blocks that were changed by this query; while the
+      number of blocks <emphasis>written</> indicates the number of
+      previously-dirtied blocks evicted from cache by this backend during
+      query processing.
       The number of blocks shown for an
       upper-level node includes those used by all its child nodes.  In text
       format, only non-zero values are printed.  This parameter may only be
index a1692f82ae85ae06cdfcf4dc7e00acd707155d72..93b1f34ca0c625ec70a7ca4baeebd41d4f189a1d 100644 (file)
@@ -1183,12 +1183,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
                {
                        bool            has_shared = (usage->shared_blks_hit > 0 ||
                                                                          usage->shared_blks_read > 0 ||
-                                                                         usage->shared_blks_written);
+                                                                         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_written);
+                                                                        usage->local_blks_dirtied > 0 ||
+                                                                        usage->local_blks_written > 0);
                        bool            has_temp = (usage->temp_blks_read > 0 ||
-                                                                       usage->temp_blks_written);
+                                                                       usage->temp_blks_written > 0);
 
                        /* Show only positive counter values. */
                        if (has_shared || has_local || has_temp)
@@ -1205,6 +1207,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
                                        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);
@@ -1220,6 +1225,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
                                        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);
@@ -1243,9 +1251,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
                {
                        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);
index 2c749b13cd8b4c3761df4c8fae6a2538f225fb56..6e9f450d6886524ec23972cfdfee6e63b909dfeb 100644 (file)
@@ -137,9 +137,11 @@ BufferUsageAccumDiff(BufferUsage *dst,
 {
        dst->shared_blks_hit += add->shared_blks_hit - sub->shared_blks_hit;
        dst->shared_blks_read += add->shared_blks_read - sub->shared_blks_read;
+       dst->shared_blks_dirtied += add->shared_blks_dirtied - sub->shared_blks_dirtied;
        dst->shared_blks_written += add->shared_blks_written - sub->shared_blks_written;
        dst->local_blks_hit += add->local_blks_hit - sub->local_blks_hit;
        dst->local_blks_read += add->local_blks_read - sub->local_blks_read;
+       dst->local_blks_dirtied += add->local_blks_dirtied - sub->local_blks_dirtied;
        dst->local_blks_written += add->local_blks_written - sub->local_blks_written;
        dst->temp_blks_read += add->temp_blks_read - sub->temp_blks_read;
        dst->temp_blks_written += add->temp_blks_written - sub->temp_blks_written;
index 1adb6d360dd5e6f7622b26a403bac387596122de..3924a51c0c6d31e1b7612ee3cf78ca04639e58aa 100644 (file)
@@ -988,6 +988,7 @@ MarkBufferDirty(Buffer buffer)
        if (dirtied)
        {
                VacuumPageDirty++;
+               pgBufferUsage.shared_blks_dirtied++;
                if (VacuumCostActive)
                        VacuumCostBalance += VacuumCostPageDirty;
                if (ProcGlobal->bgwriterLatch)
index 096d36a233bd4bf869d4b325a5026491565e1941..63c14f7300cc48d5d25be991a28c3cf80bed54f1 100644 (file)
@@ -276,6 +276,10 @@ MarkLocalBufferDirty(Buffer buffer)
        Assert(LocalRefCount[bufid] > 0);
 
        bufHdr = &LocalBufferDescriptors[bufid];
+
+       if (!(bufHdr->flags & BM_DIRTY))
+               pgBufferUsage.local_blks_dirtied++;
+
        bufHdr->flags |= BM_DIRTY;
 }
 
index 084302e4e7efef8be05ff9983bc2467cd8595e21..066f684f330ca942958d94ab59ad4bb4aecbf604 100644 (file)
@@ -20,9 +20,11 @@ typedef struct BufferUsage
 {
        long            shared_blks_hit;        /* # of shared buffer hits */
        long            shared_blks_read;               /* # of shared disk blocks read */
+       long            shared_blks_dirtied;    /* # of shared blocks dirtied */
        long            shared_blks_written;    /* # of shared disk blocks written */
        long            local_blks_hit; /* # of local buffer hits */
        long            local_blks_read;        /* # of local disk blocks read */
+       long            local_blks_dirtied;             /* # of shared blocks dirtied */
        long            local_blks_written;             /* # of local disk blocks written */
        long            temp_blks_read; /* # of temp blocks read */
        long            temp_blks_written;              /* # of temp blocks written */