]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
pg_stat_statements: Add counters for generic and custom plans
authorMichael Paquier <michael@paquier.xyz>
Thu, 31 Jul 2025 02:20:29 +0000 (11:20 +0900)
committerMichael Paquier <michael@paquier.xyz>
Thu, 31 Jul 2025 02:37:37 +0000 (11:37 +0900)
This patch adds two new counters to pg_stat_statements:
- generic_plan_calls
- custom_plan_calls

These counters track how many times a prepared statement was executed
using a generic or custom plan, respectively, providing a global
equivalent at query level, for top and non-top levels, of
pg_prepared_statements whose data is restricted to a single session.

This commit builds upon e125e360020a.  The module is bumped to version
1.13.  PGSS_FILE_HEADER is bumped as well, something that the latest
patches touching the on-disk format of the PGSS file did not actually
bother with since 2022..

Author: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Ilia Evdokimov <ilya.evdokimov@tantorlabs.com>
Reviewed-by: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Nikolay Samokhvalov <nik@postgres.ai>
Discussion: https://postgr.es/m/CAA5RZ0uFw8Y9GCFvafhC=OA8NnMqVZyzXPfv_EePOt+iv1T-qQ@mail.gmail.com

contrib/pg_stat_statements/Makefile
contrib/pg_stat_statements/expected/oldextversions.out
contrib/pg_stat_statements/expected/plancache.out [new file with mode: 0644]
contrib/pg_stat_statements/meson.build
contrib/pg_stat_statements/pg_stat_statements--1.12--1.13.sql [new file with mode: 0644]
contrib/pg_stat_statements/pg_stat_statements.c
contrib/pg_stat_statements/pg_stat_statements.control
contrib/pg_stat_statements/sql/oldextversions.sql
contrib/pg_stat_statements/sql/plancache.sql [new file with mode: 0644]
doc/src/sgml/pgstatstatements.sgml

index b2bd8794d2a14cf9a06f07b6c44f74bfac9e3750..fe0478ac55266f4ed11d2649fc75a2874219e2bb 100644 (file)
@@ -7,6 +7,7 @@ OBJS = \
 
 EXTENSION = pg_stat_statements
 DATA = pg_stat_statements--1.4.sql \
+       pg_stat_statements--1.12--1.13.sql \
        pg_stat_statements--1.11--1.12.sql pg_stat_statements--1.10--1.11.sql \
        pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
        pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
@@ -20,7 +21,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
 REGRESS = select dml cursors utility level_tracking planning \
        user_activity wal entry_timestamp privileges extended \
-       parallel cleanup oldextversions squashing
+       parallel plancache cleanup oldextversions squashing
 # Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
 # which typical installcheck users do not have (e.g. buildfarm clients).
 NO_INSTALLCHECK = 1
index de679b19711abda28123835028ad591051310e5a..726383a99d7c1b318fed04644790af949c1463bb 100644 (file)
@@ -407,4 +407,71 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
  t
 (1 row)
 
+-- New functions and views for pg_stat_statements in 1.13
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.13';
+\d pg_stat_statements
+                            View "public.pg_stat_statements"
+           Column           |           Type           | Collation | Nullable | Default 
+----------------------------+--------------------------+-----------+----------+---------
+ userid                     | oid                      |           |          | 
+ dbid                       | oid                      |           |          | 
+ toplevel                   | boolean                  |           |          | 
+ queryid                    | bigint                   |           |          | 
+ query                      | text                     |           |          | 
+ plans                      | bigint                   |           |          | 
+ total_plan_time            | double precision         |           |          | 
+ min_plan_time              | double precision         |           |          | 
+ max_plan_time              | double precision         |           |          | 
+ mean_plan_time             | double precision         |           |          | 
+ stddev_plan_time           | double precision         |           |          | 
+ calls                      | bigint                   |           |          | 
+ total_exec_time            | double precision         |           |          | 
+ min_exec_time              | double precision         |           |          | 
+ max_exec_time              | double precision         |           |          | 
+ mean_exec_time             | double precision         |           |          | 
+ stddev_exec_time           | double precision         |           |          | 
+ rows                       | bigint                   |           |          | 
+ shared_blks_hit            | bigint                   |           |          | 
+ shared_blks_read           | bigint                   |           |          | 
+ shared_blks_dirtied        | bigint                   |           |          | 
+ shared_blks_written        | bigint                   |           |          | 
+ local_blks_hit             | bigint                   |           |          | 
+ local_blks_read            | bigint                   |           |          | 
+ local_blks_dirtied         | bigint                   |           |          | 
+ local_blks_written         | bigint                   |           |          | 
+ temp_blks_read             | bigint                   |           |          | 
+ temp_blks_written          | bigint                   |           |          | 
+ shared_blk_read_time       | double precision         |           |          | 
+ shared_blk_write_time      | double precision         |           |          | 
+ local_blk_read_time        | double precision         |           |          | 
+ local_blk_write_time       | double precision         |           |          | 
+ temp_blk_read_time         | double precision         |           |          | 
+ temp_blk_write_time        | double precision         |           |          | 
+ wal_records                | bigint                   |           |          | 
+ wal_fpi                    | bigint                   |           |          | 
+ wal_bytes                  | numeric                  |           |          | 
+ wal_buffers_full           | bigint                   |           |          | 
+ jit_functions              | bigint                   |           |          | 
+ jit_generation_time        | double precision         |           |          | 
+ jit_inlining_count         | bigint                   |           |          | 
+ jit_inlining_time          | double precision         |           |          | 
+ jit_optimization_count     | bigint                   |           |          | 
+ jit_optimization_time      | double precision         |           |          | 
+ jit_emission_count         | bigint                   |           |          | 
+ jit_emission_time          | double precision         |           |          | 
+ jit_deform_count           | bigint                   |           |          | 
+ jit_deform_time            | double precision         |           |          | 
+ parallel_workers_to_launch | bigint                   |           |          | 
+ parallel_workers_launched  | bigint                   |           |          | 
+ generic_plan_calls         | bigint                   |           |          | 
+ custom_plan_calls          | bigint                   |           |          | 
+ stats_since                | timestamp with time zone |           |          | 
+ minmax_stats_since         | timestamp with time zone |           |          | 
+
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+ has_data 
+----------
+ t
+(1 row)
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/plancache.out b/contrib/pg_stat_statements/expected/plancache.out
new file mode 100644 (file)
index 0000000..e152de9
--- /dev/null
@@ -0,0 +1,224 @@
+--
+-- Tests with plan cache
+--
+-- Setup
+CREATE OR REPLACE FUNCTION select_one_func(int) RETURNS VOID AS $$
+DECLARE
+  ret INT;
+BEGIN
+  SELECT $1 INTO ret;
+END;
+$$ LANGUAGE plpgsql;
+CREATE OR REPLACE PROCEDURE select_one_proc(int) AS $$
+DECLARE
+  ret INT;
+BEGIN
+  SELECT $1 INTO ret;
+END;
+$$ LANGUAGE plpgsql;
+-- Prepared statements
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+PREPARE p1 AS SELECT $1 AS a;
+SET plan_cache_mode TO force_generic_plan;
+EXECUTE p1(1);
+ a 
+---
+ 1
+(1 row)
+
+SET plan_cache_mode TO force_custom_plan;
+EXECUTE p1(1);
+ a 
+---
+ 1
+(1 row)
+
+SELECT calls, generic_plan_calls, custom_plan_calls, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+ calls | generic_plan_calls | custom_plan_calls |                       query                        
+-------+--------------------+-------------------+----------------------------------------------------
+     2 |                  1 |                 1 | PREPARE p1 AS SELECT $1 AS a
+     1 |                  0 |                 0 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+     2 |                  0 |                 0 | SET plan_cache_mode TO $1
+(3 rows)
+
+DEALLOCATE p1;
+-- Extended query protocol
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+SELECT $1 AS a \parse p1
+SET plan_cache_mode TO force_generic_plan;
+\bind_named p1 1
+;
+ a 
+---
+ 1
+(1 row)
+
+SET plan_cache_mode TO force_custom_plan;
+\bind_named p1 1
+;
+ a 
+---
+ 1
+(1 row)
+
+SELECT calls, generic_plan_calls, custom_plan_calls, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+ calls | generic_plan_calls | custom_plan_calls |                       query                        
+-------+--------------------+-------------------+----------------------------------------------------
+     2 |                  1 |                 1 | SELECT $1 AS a
+     1 |                  0 |                 0 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+     2 |                  0 |                 0 | SET plan_cache_mode TO $1
+(3 rows)
+
+\close_prepared p1
+-- EXPLAIN [ANALYZE] EXECUTE
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+PREPARE p1 AS SELECT $1;
+SET plan_cache_mode TO force_generic_plan;
+EXPLAIN (COSTS OFF) EXECUTE p1(1);
+ QUERY PLAN 
+------------
+ Result
+(1 row)
+
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) EXECUTE p1(1);
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
+(1 row)
+
+SET plan_cache_mode TO force_custom_plan;
+EXPLAIN (COSTS OFF) EXECUTE p1(1);
+ QUERY PLAN 
+------------
+ Result
+(1 row)
+
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) EXECUTE p1(1);
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
+(1 row)
+
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+ calls | generic_plan_calls | custom_plan_calls | toplevel |                                      query                                       
+-------+--------------------+-------------------+----------+----------------------------------------------------------------------------------
+     2 |                  0 |                 0 | t        | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) EXECUTE p1(1)
+     2 |                  0 |                 0 | t        | EXPLAIN (COSTS OFF) EXECUTE p1(1)
+     4 |                  2 |                 2 | f        | PREPARE p1 AS SELECT $1
+     1 |                  0 |                 0 | t        | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+     2 |                  0 |                 0 | t        | SET plan_cache_mode TO $1
+(5 rows)
+
+RESET pg_stat_statements.track;
+DEALLOCATE p1;
+-- Functions/procedures
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+SET plan_cache_mode TO force_generic_plan;
+SELECT select_one_func(1);
+ select_one_func 
+-----------------
+(1 row)
+
+CALL select_one_proc(1);
+SET plan_cache_mode TO force_custom_plan;
+SELECT select_one_func(1);
+ select_one_func 
+-----------------
+(1 row)
+
+CALL select_one_proc(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+ calls | generic_plan_calls | custom_plan_calls | toplevel |                       query                        
+-------+--------------------+-------------------+----------+----------------------------------------------------
+     2 |                  0 |                 0 | t        | CALL select_one_proc($1)
+     4 |                  2 |                 2 | f        | SELECT $1
+     1 |                  0 |                 0 | t        | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+     2 |                  0 |                 0 | t        | SELECT select_one_func($1)
+     2 |                  0 |                 0 | t        | SET plan_cache_mode TO $1
+(5 rows)
+
+--
+-- EXPLAIN [ANALYZE] EXECUTE + functions/procedures
+--
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t 
+---
+ t
+(1 row)
+
+SET plan_cache_mode TO force_generic_plan;
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func(1);
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT select_one_func(1);
+ QUERY PLAN 
+------------
+ Result
+(1 row)
+
+CALL select_one_proc(1);
+SET plan_cache_mode TO force_custom_plan;
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func(1);
+            QUERY PLAN             
+-----------------------------------
+ Result (actual rows=1.00 loops=1)
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT select_one_func(1);
+ QUERY PLAN 
+------------
+ Result
+(1 row)
+
+CALL select_one_proc(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C", toplevel;
+ calls | generic_plan_calls | custom_plan_calls | toplevel |                                             query                                              
+-------+--------------------+-------------------+----------+------------------------------------------------------------------------------------------------
+     2 |                  0 |                 0 | t        | CALL select_one_proc($1)
+     2 |                  0 |                 0 | t        | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func($1)
+     4 |                  0 |                 0 | f        | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func($1);
+     2 |                  0 |                 0 | t        | EXPLAIN (COSTS OFF) SELECT select_one_func($1)
+     4 |                  2 |                 2 | f        | SELECT $1
+     1 |                  0 |                 0 | t        | SELECT pg_stat_statements_reset() IS NOT NULL AS t
+     2 |                  0 |                 0 | t        | SET plan_cache_mode TO $1
+(7 rows)
+
+RESET pg_stat_statements.track;
+--
+-- Cleanup
+--
+DROP FUNCTION select_one_func(int);
+DROP PROCEDURE select_one_proc(int);
index 01a6cbdcf6139f9534c6629c9183eb3530372650..7b8bfbb1de78ce60479109c9002ca402cf9bef5a 100644 (file)
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
 install_data(
   'pg_stat_statements.control',
   'pg_stat_statements--1.4.sql',
+  'pg_stat_statements--1.12--1.13.sql',
   'pg_stat_statements--1.11--1.12.sql',
   'pg_stat_statements--1.10--1.11.sql',
   'pg_stat_statements--1.9--1.10.sql',
@@ -54,6 +55,7 @@ tests += {
       'privileges',
       'extended',
       'parallel',
+      'plancache',
       'cleanup',
       'oldextversions',
       'squashing',
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.12--1.13.sql b/contrib/pg_stat_statements/pg_stat_statements--1.12--1.13.sql
new file mode 100644 (file)
index 0000000..2f0eaf1
--- /dev/null
@@ -0,0 +1,78 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.12--1.13.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.13'" 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 toplevel bool,
+    OUT queryid bigint,
+    OUT query text,
+    OUT plans int8,
+    OUT total_plan_time float8,
+    OUT min_plan_time float8,
+    OUT max_plan_time float8,
+    OUT mean_plan_time float8,
+    OUT stddev_plan_time float8,
+    OUT calls int8,
+    OUT total_exec_time float8,
+    OUT min_exec_time float8,
+    OUT max_exec_time float8,
+    OUT mean_exec_time float8,
+    OUT stddev_exec_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 shared_blk_read_time float8,
+    OUT shared_blk_write_time float8,
+    OUT local_blk_read_time float8,
+    OUT local_blk_write_time float8,
+    OUT temp_blk_read_time float8,
+    OUT temp_blk_write_time float8,
+    OUT wal_records int8,
+    OUT wal_fpi int8,
+    OUT wal_bytes numeric,
+    OUT wal_buffers_full int8,
+    OUT jit_functions int8,
+    OUT jit_generation_time float8,
+    OUT jit_inlining_count int8,
+    OUT jit_inlining_time float8,
+    OUT jit_optimization_count int8,
+    OUT jit_optimization_time float8,
+    OUT jit_emission_count int8,
+    OUT jit_emission_time float8,
+    OUT jit_deform_count int8,
+    OUT jit_deform_time float8,
+    OUT parallel_workers_to_launch int8,
+    OUT parallel_workers_launched int8,
+    OUT generic_plan_calls int8,
+    OUT custom_plan_calls int8,
+    OUT stats_since timestamp with time zone,
+    OUT minmax_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_13'
+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;
index e7857f81ec0576a0a0641f992ab787692d22482b..9fc9635d3300d0dc41114e83058d9fb3eb7dd22a 100644 (file)
@@ -85,7 +85,7 @@ PG_MODULE_MAGIC_EXT(
 #define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
 
 /* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20250731;
 
 /* PostgreSQL major version number, changes in which invalidate all entries */
 static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -114,6 +114,7 @@ typedef enum pgssVersion
        PGSS_V1_10,
        PGSS_V1_11,
        PGSS_V1_12,
+       PGSS_V1_13,
 } pgssVersion;
 
 typedef enum pgssStoreKind
@@ -210,6 +211,8 @@ typedef struct Counters
                                                                                         * to be launched */
        int64           parallel_workers_launched;      /* # of parallel workers actually
                                                                                         * launched */
+       int64           generic_plan_calls; /* number of calls using a generic plan */
+       int64           custom_plan_calls;      /* number of calls using a custom plan */
 } Counters;
 
 /*
@@ -323,6 +326,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
 PG_FUNCTION_INFO_V1(pg_stat_statements_1_12);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_13);
 PG_FUNCTION_INFO_V1(pg_stat_statements);
 PG_FUNCTION_INFO_V1(pg_stat_statements_info);
 
@@ -355,7 +359,8 @@ static void pgss_store(const char *query, int64 queryId,
                                           const struct JitInstrumentation *jitusage,
                                           JumbleState *jstate,
                                           int parallel_workers_to_launch,
-                                          int parallel_workers_launched);
+                                          int parallel_workers_launched,
+                                          PlannedStmtOrigin planOrigin);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
                                                                                pgssVersion api_version,
                                                                                bool showtext);
@@ -877,7 +882,8 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query, JumbleState *jstate)
                                   NULL,
                                   jstate,
                                   0,
-                                  0);
+                                  0,
+                                  PLAN_STMT_UNKNOWN);
 }
 
 /*
@@ -957,7 +963,8 @@ pgss_planner(Query *parse,
                                   NULL,
                                   NULL,
                                   0,
-                                  0);
+                                  0,
+                                  result->planOrigin);
        }
        else
        {
@@ -1091,7 +1098,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
                                   queryDesc->estate->es_jit ? &queryDesc->estate->es_jit->instr : NULL,
                                   NULL,
                                   queryDesc->estate->es_parallel_workers_to_launch,
-                                  queryDesc->estate->es_parallel_workers_launched);
+                                  queryDesc->estate->es_parallel_workers_launched,
+                                  queryDesc->plannedstmt->planOrigin);
        }
 
        if (prev_ExecutorEnd)
@@ -1224,7 +1232,8 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                   NULL,
                                   NULL,
                                   0,
-                                  0);
+                                  0,
+                                  pstmt->planOrigin);
        }
        else
        {
@@ -1287,7 +1296,8 @@ pgss_store(const char *query, int64 queryId,
                   const struct JitInstrumentation *jitusage,
                   JumbleState *jstate,
                   int parallel_workers_to_launch,
-                  int parallel_workers_launched)
+                  int parallel_workers_launched,
+                  PlannedStmtOrigin planOrigin)
 {
        pgssHashKey key;
        pgssEntry  *entry;
@@ -1495,6 +1505,12 @@ pgss_store(const char *query, int64 queryId,
                entry->counters.parallel_workers_to_launch += parallel_workers_to_launch;
                entry->counters.parallel_workers_launched += parallel_workers_launched;
 
+               /* plan cache counters */
+               if (planOrigin == PLAN_STMT_CACHE_GENERIC)
+                       entry->counters.generic_plan_calls++;
+               else if (planOrigin == PLAN_STMT_CACHE_CUSTOM)
+                       entry->counters.custom_plan_calls++;
+
                SpinLockRelease(&entry->mutex);
        }
 
@@ -1562,7 +1578,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
 #define PG_STAT_STATEMENTS_COLS_V1_10  43
 #define PG_STAT_STATEMENTS_COLS_V1_11  49
 #define PG_STAT_STATEMENTS_COLS_V1_12  52
-#define PG_STAT_STATEMENTS_COLS                        52      /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_13  54
+#define PG_STAT_STATEMENTS_COLS                        54      /* maximum of above */
 
 /*
  * Retrieve statement statistics.
@@ -1574,6 +1591,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_13(PG_FUNCTION_ARGS)
+{
+       bool            showtext = PG_GETARG_BOOL(0);
+
+       pg_stat_statements_internal(fcinfo, PGSS_V1_13, showtext);
+
+       return (Datum) 0;
+}
+
 Datum
 pg_stat_statements_1_12(PG_FUNCTION_ARGS)
 {
@@ -1732,6 +1759,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
                        if (api_version != PGSS_V1_12)
                                elog(ERROR, "incorrect number of output arguments");
                        break;
+               case PG_STAT_STATEMENTS_COLS_V1_13:
+                       if (api_version != PGSS_V1_13)
+                               elog(ERROR, "incorrect number of output arguments");
+                       break;
                default:
                        elog(ERROR, "incorrect number of output arguments");
        }
@@ -1984,6 +2015,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
                        values[i++] = Int64GetDatumFast(tmp.parallel_workers_to_launch);
                        values[i++] = Int64GetDatumFast(tmp.parallel_workers_launched);
                }
+               if (api_version >= PGSS_V1_13)
+               {
+                       values[i++] = Int64GetDatumFast(tmp.generic_plan_calls);
+                       values[i++] = Int64GetDatumFast(tmp.custom_plan_calls);
+               }
                if (api_version >= PGSS_V1_11)
                {
                        values[i++] = TimestampTzGetDatum(stats_since);
@@ -1999,6 +2035,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
                                         api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
                                         api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
                                         api_version == PGSS_V1_12 ? PG_STAT_STATEMENTS_COLS_V1_12 :
+                                        api_version == PGSS_V1_13 ? PG_STAT_STATEMENTS_COLS_V1_13 :
                                         -1 /* fail if you forget to update this assert */ ));
 
                tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
index d45ebc12e3605f0f95500c82c50d07cee8989a52..2eee0ceffa894f14e78bee6c8ac38beff263ee31 100644 (file)
@@ -1,5 +1,5 @@
 # pg_stat_statements extension
 comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.12'
+default_version = '1.13'
 module_pathname = '$libdir/pg_stat_statements'
 relocatable = true
index 13b8ca28586d124b707b99c8fad19feb8dd7841d..e416efe9ffbee1341b6c6a3d2e47c6bd7a7a64a0 100644 (file)
@@ -63,4 +63,9 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.12';
 \d pg_stat_statements
 SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
 
+-- New functions and views for pg_stat_statements in 1.13
+AlTER EXTENSION pg_stat_statements UPDATE TO '1.13';
+\d pg_stat_statements
+SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/plancache.sql b/contrib/pg_stat_statements/sql/plancache.sql
new file mode 100644 (file)
index 0000000..160ced7
--- /dev/null
@@ -0,0 +1,94 @@
+--
+-- Tests with plan cache
+--
+
+-- Setup
+CREATE OR REPLACE FUNCTION select_one_func(int) RETURNS VOID AS $$
+DECLARE
+  ret INT;
+BEGIN
+  SELECT $1 INTO ret;
+END;
+$$ LANGUAGE plpgsql;
+CREATE OR REPLACE PROCEDURE select_one_proc(int) AS $$
+DECLARE
+  ret INT;
+BEGIN
+  SELECT $1 INTO ret;
+END;
+$$ LANGUAGE plpgsql;
+
+-- Prepared statements
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+PREPARE p1 AS SELECT $1 AS a;
+SET plan_cache_mode TO force_generic_plan;
+EXECUTE p1(1);
+SET plan_cache_mode TO force_custom_plan;
+EXECUTE p1(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+DEALLOCATE p1;
+
+-- Extended query protocol
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+SELECT $1 AS a \parse p1
+SET plan_cache_mode TO force_generic_plan;
+\bind_named p1 1
+;
+SET plan_cache_mode TO force_custom_plan;
+\bind_named p1 1
+;
+SELECT calls, generic_plan_calls, custom_plan_calls, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+\close_prepared p1
+
+-- EXPLAIN [ANALYZE] EXECUTE
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+PREPARE p1 AS SELECT $1;
+SET plan_cache_mode TO force_generic_plan;
+EXPLAIN (COSTS OFF) EXECUTE p1(1);
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) EXECUTE p1(1);
+SET plan_cache_mode TO force_custom_plan;
+EXPLAIN (COSTS OFF) EXECUTE p1(1);
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) EXECUTE p1(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+RESET pg_stat_statements.track;
+DEALLOCATE p1;
+
+-- Functions/procedures
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+SET plan_cache_mode TO force_generic_plan;
+SELECT select_one_func(1);
+CALL select_one_proc(1);
+SET plan_cache_mode TO force_custom_plan;
+SELECT select_one_func(1);
+CALL select_one_proc(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C";
+
+--
+-- EXPLAIN [ANALYZE] EXECUTE + functions/procedures
+--
+SET pg_stat_statements.track = 'all';
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+SET plan_cache_mode TO force_generic_plan;
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func(1);
+EXPLAIN (COSTS OFF) SELECT select_one_func(1);
+CALL select_one_proc(1);
+SET plan_cache_mode TO force_custom_plan;
+EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF, BUFFERS OFF) SELECT select_one_func(1);
+EXPLAIN (COSTS OFF) SELECT select_one_func(1);
+CALL select_one_proc(1);
+SELECT calls, generic_plan_calls, custom_plan_calls, toplevel, query FROM pg_stat_statements
+  ORDER BY query COLLATE "C", toplevel;
+
+RESET pg_stat_statements.track;
+
+--
+-- Cleanup
+--
+DROP FUNCTION select_one_func(int);
+DROP PROCEDURE select_one_proc(int);
index 7baa07dcdbf7f56ded064365062f8bcf3d88024f..d753de5836efb1e0d06eb23596d285806994009f 100644 (file)
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>generic_plan_calls</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement has been executed using a generic plan
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>custom_plan_calls</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times the statement has been executed using a custom plan
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>stats_since</structfield> <type>timestamp with time zone</type>