diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 051ce46f0c55..db33d9ffe1c5 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -4,7 +4,8 @@ MODULE_big = pg_stat_statements
OBJS = pg_stat_statements.o $(WIN32RES)
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.6--1.7.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
pg_stat_statements--1.1--1.2.sql pg_stat_statements--1.0--1.1.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index c759763be217..8517b124e4bb 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -593,4 +593,68 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
+--
+-- [re]plan counting
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+CREATE TABLE test ();
+PREPARE prep1 AS SELECT COUNT(*) FROM test;;
+EXECUTE prep1;
+ count
+-------
+ 0
+(1 row)
+
+EXECUTE prep1;
+ count
+-------
+ 0
+(1 row)
+
+EXECUTE prep1;
+ count
+-------
+ 0
+(1 row)
+
+ALTER TABLE test ADD COLUMN x int;
+EXECUTE prep1;
+ count
+-------
+ 0
+(1 row)
+
+SELECT 42;
+ ?column?
+----------
+ 42
+(1 row)
+
+SELECT 42;
+ ?column?
+----------
+ 42
+(1 row)
+
+SELECT 42;
+ ?column?
+----------
+ 42
+(1 row)
+
+SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+ query | plans | calls | rows
+--------------------------------------------+-------+-------+------
+ ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
+ CREATE TABLE test () | 0 | 1 | 0
+ PREPARE prep1 AS SELECT COUNT(*) FROM test | 2 | 4 | 4
+ SELECT $1 | 3 | 3 | 3
+ SELECT pg_stat_statements_reset() | 0 | 1 | 1
+(5 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql b/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql
new file mode 100644
index 000000000000..94552009a0a3
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql
@@ -0,0 +1,52 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.7--1.8.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.8'" 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(boolean);
+
+/* Then we can drop them */
+DROP VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_time float8,
+ OUT max_time float8,
+ OUT mean_time float8,
+ OUT stddev_time float8,
+ 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,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_8'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
+
+
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 16b8074a0067..12aed5bdb22f 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -1,7 +1,8 @@
/*-------------------------------------------------------------------------
*
* pg_stat_statements.c
- * Track statement execution times across a whole database cluster.
+ * Track statement planning and execution times as well as resources
+ * consumption (like block reads, ...) across a whole cluster.
*
* Execution costs are totalled for each distinct source query, and kept in
* a shared hashtable. (We track only as many distinct queries as will fit
@@ -66,6 +67,7 @@
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
#include "parser/scanner.h"
@@ -120,7 +122,8 @@ typedef enum pgssVersion
PGSS_V1_0 = 0,
PGSS_V1_1,
PGSS_V1_2,
- PGSS_V1_3
+ PGSS_V1_3,
+ PGSS_V1_8
} pgssVersion;
/*
@@ -145,11 +148,14 @@ typedef struct pgssHashKey
typedef struct Counters
{
int64 calls; /* # of times executed */
- double total_time; /* total execution time, in msec */
+ double total_exec_time; /* total execution time, in msec */
double min_time; /* minimum execution time in msec */
double max_time; /* maximum execution time in msec */
double mean_time; /* mean execution time in msec */
double sum_var_time; /* sum of variances in execution time in msec */
+ int64 plans; /* # of times planned */
+ double total_plan_time; /* total planning time, in msec */
+ double total_time; /* total planning and execution time, in msec */
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 */
@@ -238,6 +244,7 @@ static int nested_level = 0;
/* Saved hook values in case of unload */
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
+static planner_hook_type prev_planner_hook = NULL;
static post_parse_analyze_hook_type prev_post_parse_analyze_hook = NULL;
static ExecutorStart_hook_type prev_ExecutorStart = NULL;
static ExecutorRun_hook_type prev_ExecutorRun = NULL;
@@ -268,6 +275,7 @@ static const struct config_enum_entry track_options[] =
static int pgss_max; /* max # statements to track */
static int pgss_track; /* tracking level */
+static bool pgss_track_planning; /* whether to track planning duration */
static bool pgss_track_utility; /* whether to track utility commands */
static bool pgss_save; /* whether to save stats across shutdown */
@@ -293,10 +301,14 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_reset);
PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
PG_FUNCTION_INFO_V1(pg_stat_statements);
static void pgss_shmem_startup(void);
static void pgss_shmem_shutdown(int code, Datum arg);
+static PlannedStmt *pgss_planner_hook(Query *parse,
+ int cursorOptions,
+ ParamListInfo boundParams);
static void pgss_post_parse_analyze(ParseState *pstate, Query *query);
static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
static void pgss_ExecutorRun(QueryDesc *queryDesc,
@@ -311,7 +323,9 @@ static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
static uint64 pgss_hash_string(const char *str, int len);
static void pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
- double total_time, uint64 rows,
+ double total_exec_time,
+ double total_plan_time,
+ uint64 rows,
const BufferUsage *bufusage,
pgssJumbleState *jstate);
static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
@@ -398,7 +412,18 @@ _PG_init(void)
NULL,
NULL);
- DefineCustomBoolVariable("pg_stat_statements.save",
+ DefineCustomBoolVariable("pg_stat_statements.track_planning",
+ "Selects whether planning duration is tracked by pg_stat_statements.",
+ NULL,
+ &pgss_track_planning,
+ true,
+ PGC_SUSET,
+ 0,
+ NULL,
+ NULL,
+ NULL);
+
+ DefineCustomBoolVariable("pg_stat_statements.save",
"Save pg_stat_statements statistics across server shutdowns.",
NULL,
&pgss_save,
@@ -424,6 +449,8 @@ _PG_init(void)
*/
prev_shmem_startup_hook = shmem_startup_hook;
shmem_startup_hook = pgss_shmem_startup;
+ prev_planner_hook = planner_hook;
+ planner_hook = pgss_planner_hook;
prev_post_parse_analyze_hook = post_parse_analyze_hook;
post_parse_analyze_hook = pgss_post_parse_analyze;
prev_ExecutorStart = ExecutorStart_hook;
@@ -447,6 +474,7 @@ _PG_fini(void)
/* Uninstall hooks. */
shmem_startup_hook = prev_shmem_startup_hook;
post_parse_analyze_hook = prev_post_parse_analyze_hook;
+ planner_hook = prev_planner_hook;
ExecutorStart_hook = prev_ExecutorStart;
ExecutorRun_hook = prev_ExecutorRun;
ExecutorFinish_hook = prev_ExecutorFinish;
@@ -831,16 +859,111 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
* normalized form of the query string. If there were no such constants,
* the normalized string would be the same as the query text anyway, so
* there's no need for an early entry.
+ * If track_planning is enabled, a hash table entry is created for the query,
+ * so that it is prepared for planning hook (where query text is not available).
*/
- if (jstate.clocations_count > 0)
+ if (jstate.clocations_count > 0 || pgss_track_planning)
pgss_store(pstate->p_sourcetext,
- query->queryId,
- query->stmt_location,
- query->stmt_len,
+ query->queryId,
+ query->stmt_location,
+ query->stmt_len,
+ 0,
+ 0,
+ 0,
+ NULL,
+ &jstate);
+}
+
+/*
+ * Planner hook: forward to regular planner, but measure planning time.
+ */
+static PlannedStmt *
+pgss_planner_hook(Query *parse,
+ int cursorOptions,
+ ParamListInfo boundParams)
+{
+ PlannedStmt *result;
+ BufferUsage bufusage_start,
+ bufusage;
+
+ bufusage_start = pgBufferUsage;
+ if (pgss_enabled() && pgss_track_planning)
+ {
+ instr_time start;
+ instr_time duration;
+
+ INSTR_TIME_SET_CURRENT(start);
+
+ nested_level++;
+ PG_TRY();
+ {
+ if (prev_planner_hook)
+ result = prev_planner_hook(parse, cursorOptions, boundParams);
+ else
+ result = standard_planner(parse, cursorOptions, boundParams);
+ nested_level--;
+ }
+ PG_CATCH();
+ {
+ nested_level--;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ INSTR_TIME_SET_CURRENT(duration);
+ INSTR_TIME_SUBTRACT(duration, start);
+
+ /* calc differences of buffer counters. */
+ bufusage.shared_blks_hit =
+ pgBufferUsage.shared_blks_hit - bufusage_start.shared_blks_hit;
+ bufusage.shared_blks_read =
+ pgBufferUsage.shared_blks_read - bufusage_start.shared_blks_read;
+ bufusage.shared_blks_dirtied =
+ pgBufferUsage.shared_blks_dirtied - bufusage_start.shared_blks_dirtied;
+ bufusage.shared_blks_written =
+ pgBufferUsage.shared_blks_written - bufusage_start.shared_blks_written;
+ bufusage.local_blks_hit =
+ pgBufferUsage.local_blks_hit - bufusage_start.local_blks_hit;
+ bufusage.local_blks_read =
+ pgBufferUsage.local_blks_read - bufusage_start.local_blks_read;
+ bufusage.local_blks_dirtied =
+ pgBufferUsage.local_blks_dirtied - bufusage_start.local_blks_dirtied;
+ bufusage.local_blks_written =
+ pgBufferUsage.local_blks_written - bufusage_start.local_blks_written;
+ bufusage.temp_blks_read =
+ pgBufferUsage.temp_blks_read - bufusage_start.temp_blks_read;
+ bufusage.temp_blks_written =
+ pgBufferUsage.temp_blks_written - bufusage_start.temp_blks_written;
+ bufusage.blk_read_time = pgBufferUsage.blk_read_time;
+ INSTR_TIME_SUBTRACT(bufusage.blk_read_time, bufusage_start.blk_read_time);
+ bufusage.blk_write_time = pgBufferUsage.blk_write_time;
+ INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time);
+
+ /*
+ * we only store planning duration, query text has been initialized during
+ * previous pgss_post_parse_analyze as it not available inside
+ * pgss_planner_hook.
+ */
+
+ pgss_store("",
+ parse->queryId,
+ -1,
0,
0,
- NULL,
- &jstate);
+ INSTR_TIME_GET_MILLISEC(duration),
+ 0,
+ &bufusage,
+ NULL);
+ }
+ else
+ {
+ if (prev_planner_hook)
+ result = prev_planner_hook(parse, cursorOptions, boundParams);
+ else
+ result = standard_planner(parse, cursorOptions, boundParams);
+ }
+
+ return result;
}
/*
@@ -945,6 +1068,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
queryDesc->plannedstmt->stmt_location,
queryDesc->plannedstmt->stmt_len,
queryDesc->totaltime->total * 1000.0, /* convert to msec */
+ 0,
queryDesc->estate->es_processed,
&queryDesc->totaltime->bufusage,
NULL);
@@ -977,8 +1101,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
* string), and then a different one with the same query string (but hash
* calculated from the query tree) would be used to accumulate costs of
* ensuing EXECUTEs. This would be confusing, and inconsistent with other
- * cases where planning time is not included at all.
- *
+ * cases where planning time is not included at all.
+ *
* Likewise, we don't track execution of DEALLOCATE.
*/
if (pgss_track_utility && pgss_enabled() &&
@@ -1056,6 +1180,7 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
pstmt->stmt_location,
pstmt->stmt_len,
INSTR_TIME_GET_MILLISEC(duration),
+ 0,
rows,
&bufusage,
NULL);
@@ -1093,12 +1218,14 @@ pgss_hash_string(const char *str, int len)
*
* If jstate is not NULL then we're trying to create an entry for which
* we have no statistics as yet; we just want to record the normalized
- * query string. total_time, rows, bufusage are ignored in this case.
+ * query string. total_exec_time, rows, bufusage are ignored in this case.
*/
static void
pgss_store(const char *query, uint64 queryId,
int query_location, int query_len,
- double total_time, uint64 rows,
+ double exec_time,
+ double plan_time,
+ uint64 rows,
const BufferUsage *bufusage,
pgssJumbleState *jstate)
{
@@ -1109,6 +1236,9 @@ pgss_store(const char *query, uint64 queryId,
Assert(query != NULL);
+ /* updating counters for execute OR planning */
+ Assert(plan_time > 0 && exec_time > 0);
+
/* Safety check... */
if (!pgss || !pgss_hash)
return;
@@ -1153,8 +1283,9 @@ pgss_store(const char *query, uint64 queryId,
queryId = pgss_hash_string(query, query_len);
/*
- * If we are unlucky enough to get a hash of zero(invalid), use queryID
- * as 2 instead, queryID 1 is already in use for normal statements.
+ * If we are unlucky enough to get a hash of zero(invalid), use
+ * queryID as 2 instead, queryID 1 is already in use for normal
+ * statements.
*/
if (queryId == UINT64CONST(0))
queryId = UINT64CONST(2);
@@ -1249,47 +1380,60 @@ pgss_store(const char *query, uint64 queryId,
if (e->counters.calls == 0)
e->counters.usage = USAGE_INIT;
- e->counters.calls += 1;
- e->counters.total_time += total_time;
- if (e->counters.calls == 1)
+ if (plan_time == 0)
{
- e->counters.min_time = total_time;
- e->counters.max_time = total_time;
- e->counters.mean_time = total_time;
+ /* updating counters for executions */
+ e->counters.calls += 1;
+ e->counters.total_exec_time += exec_time;
+ e->counters.total_time += exec_time;
+
+ if (e->counters.calls == 1 )
+ {
+ e->counters.min_time = exec_time;
+ e->counters.max_time = exec_time;
+ e->counters.mean_time = exec_time;
+ }
+ else
+ {
+ /*
+ * Welford's method for accurately computing variance. See
+ *
+ */
+ double old_mean = e->counters.mean_time;
+
+ e->counters.mean_time +=
+ (exec_time - old_mean) / e->counters.calls;
+ e->counters.sum_var_time +=
+ (exec_time - old_mean) * (exec_time - e->counters.mean_time);
+
+ /* calculate min and max time */
+ if (e->counters.min_time > exec_time)
+ e->counters.min_time = exec_time;
+ if (e->counters.max_time < exec_time)
+ e->counters.max_time = exec_time;
+ }
+ 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;
+ e->counters.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
+ e->counters.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
+ e->counters.usage += USAGE_EXEC(total_exec_time);
}
else
{
- /*
- * Welford's method for accurately computing variance. See
- *
- */
- double old_mean = e->counters.mean_time;
-
- e->counters.mean_time +=
- (total_time - old_mean) / e->counters.calls;
- e->counters.sum_var_time +=
- (total_time - old_mean) * (total_time - e->counters.mean_time);
-
- /* calculate min and max time */
- if (e->counters.min_time > total_time)
- e->counters.min_time = total_time;
- if (e->counters.max_time < total_time)
- e->counters.max_time = total_time;
+ /* updating counters for planning */
+ e->counters.plans += 1;
+ e->counters.total_plan_time += plan_time;
+ e->counters.total_time += plan_time;
}
- 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;
- e->counters.blk_read_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_read_time);
- e->counters.blk_write_time += INSTR_TIME_GET_MILLISEC(bufusage->blk_write_time);
- e->counters.usage += USAGE_EXEC(total_time);
SpinLockRelease(&e->mutex);
}
@@ -1337,7 +1481,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_1 18
#define PG_STAT_STATEMENTS_COLS_V1_2 19
#define PG_STAT_STATEMENTS_COLS_V1_3 23
-#define PG_STAT_STATEMENTS_COLS 23 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_8 26
+#define PG_STAT_STATEMENTS_COLS 26 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1349,6 +1494,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_8(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_8, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_3(PG_FUNCTION_ARGS)
{
@@ -1455,6 +1610,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_3)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_8:
+ if (api_version != PGSS_V1_8)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1613,8 +1772,16 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (tmp.calls == 0)
continue;
+ if (api_version >= PGSS_V1_8)
+ {
+ values[i++] = Int64GetDatumFast(tmp.plans);
+ values[i++] = Float8GetDatumFast(tmp.total_plan_time);
+ }
values[i++] = Int64GetDatumFast(tmp.calls);
- values[i++] = Float8GetDatumFast(tmp.total_time);
+ if (api_version >= PGSS_V1_8)
+ values[i++] = Float8GetDatumFast(tmp.total_exec_time);
+ if (api_version < PGSS_V1_8)
+ values[i++] = Float8GetDatumFast(tmp.total_time);
if (api_version >= PGSS_V1_3)
{
values[i++] = Float8GetDatumFast(tmp.min_time);
@@ -1633,6 +1800,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
stddev = 0.0;
values[i++] = Float8GetDatumFast(stddev);
}
+ if (api_version >= PGSS_V1_8)
+ values[i++] = Float8GetDatumFast(tmp.total_time);
values[i++] = Int64GetDatumFast(tmp.rows);
values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
@@ -1656,6 +1825,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
+ api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 14cb42235437..7fb20df88627 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track execution statistics of all SQL statements executed'
-default_version = '1.7'
+default_version = '1.8'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index aef890d893d8..4d7d6e584db2 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -259,4 +259,20 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
+--
+-- [re]plan counting
+--
+SELECT pg_stat_statements_reset();
+CREATE TABLE test ();
+PREPARE prep1 AS SELECT COUNT(*) FROM test;;
+EXECUTE prep1;
+EXECUTE prep1;
+EXECUTE prep1;
+ALTER TABLE test ADD COLUMN x int;
+EXECUTE prep1;
+SELECT 42;
+SELECT 42;
+SELECT 42;
+SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 26bb82da4a8b..368daf3e3922 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -9,7 +9,8 @@
The pg_stat_statements module provides a means for
- tracking execution statistics of all SQL statements executed by a server.
+ tracking planning and execution statistics of all SQL statements executed by
+ a server.
@@ -82,6 +83,20 @@
Text of a representative statement
+
+ plans
+ bigint
+
+ Number of times the statement was planned
+
+
+
+ total_plan_time
+ double precision
+
+ Total time spent planning the statement, in milliseconds
+
+
calls
bigint
@@ -89,39 +104,46 @@
Number of times executed
+
+ total_exec_time
+ double precision
+
+ Total time spent executing the statement, in milliseconds
+
+
total_time
double precision
- Total time spent in the statement, in milliseconds
+ Total time spent planning and executing the statement, in milliseconds
min_time
double precision
- Minimum time spent in the statement, in milliseconds
+ Minimum time spent executing the statement, in milliseconds
max_time
double precision
- Maximum time spent in the statement, in milliseconds
+ Maximum time spent executing the statement, in milliseconds
mean_time
double precision
- Mean time spent in the statement, in milliseconds
+ Mean time spent executing the statement, in milliseconds
stddev_time
double precision
- Population standard deviation of time spent in the statement, in milliseconds
+ Population standard deviation of time spent executing the statement, in milliseconds
@@ -431,6 +453,21 @@
+
+
+ pg_stat_statements.track_planning (boolean)
+
+
+
+
+ pg_stat_statements.track_planning controls whether
+ planning operations and duration are tracked by the module.
+ The default value is on.
+ Only superusers can change this setting.
+
+
+
+
pg_stat_statements.track_utility (boolean)