Hi,

pg_stat_statements is basically very helpful to find out slow queries.
But since it doesn't report the time spent in the planner, we cannot
find out slow queries which take most time to do query planning, from
pg_stat_statements. Is there any reason why pg_stat_statements doesn't
collect the planning time?

Attached patch extends pg_stat_statements so that it reports the
planning time. Thought?

In the patch, I didn't change the column name "total_time" meaning
the time spent in the executor because of the backward compatibility.
But once new column "plan_time" is added, "total_time" is confusing and
ISTM it should be renamed...

Regards,

-- 
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
*** 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
***************
*** 17,22 **** CREATE FUNCTION pg_stat_statements(
--- 17,23 ----
      OUT dbid oid,
      OUT query text,
      OUT calls int8,
+     OUT plan_time float8,
      OUT total_time float8,
      OUT rows int8,
      OUT shared_blks_hit int8,
*** a/contrib/pg_stat_statements/pg_stat_statements--1.1.sql
--- b/contrib/pg_stat_statements/pg_stat_statements--1.1.sql
***************
*** 14,19 **** CREATE FUNCTION pg_stat_statements(
--- 14,20 ----
      OUT dbid oid,
      OUT query text,
      OUT calls int8,
+     OUT plan_time float8,
      OUT total_time float8,
      OUT rows int8,
      OUT shared_blks_hit int8,
*** a/contrib/pg_stat_statements/pg_stat_statements.c
--- b/contrib/pg_stat_statements/pg_stat_statements.c
***************
*** 1,7 ****
  /*-------------------------------------------------------------------------
   *
   * pg_stat_statements.c
!  *		Track statement execution times across a whole database cluster.
   *
   * Note about locking issues: to create or delete an entry in the shared
   * hashtable, one must hold pgss->lock exclusively.  Modifying any field
--- 1,7 ----
  /*-------------------------------------------------------------------------
   *
   * pg_stat_statements.c
!  *		Track statement planning and execution times across a whole database cluster.
   *
   * Note about locking issues: to create or delete an entry in the shared
   * hashtable, one must hold pgss->lock exclusively.  Modifying any field
***************
*** 27,32 ****
--- 27,33 ----
  #include "funcapi.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
+ #include "optimizer/planner.h"
  #include "pgstat.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
***************
*** 73,78 **** typedef struct pgssHashKey
--- 74,80 ----
  typedef struct Counters
  {
  	int64		calls;			/* # of times executed */
+ 	double		plan_time;		/* total planning time in seconds */
  	double		total_time;		/* total execution time in seconds */
  	int64		rows;			/* total # of retrieved or affected rows */
  	int64		shared_blks_hit;	/* # of shared buffer hits */
***************
*** 118,123 **** static int	nested_level = 0;
--- 120,126 ----
  
  /* 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 ExecutorStart_hook_type prev_ExecutorStart = NULL;
  static ExecutorRun_hook_type prev_ExecutorRun = NULL;
  static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
***************
*** 168,173 **** PG_FUNCTION_INFO_V1(pg_stat_statements);
--- 171,178 ----
  
  static void pgss_shmem_startup(void);
  static void pgss_shmem_shutdown(int code, Datum arg);
+ static PlannedStmt *pgss_planner(Query *parse, int cursorOptions,
+ 				 ParamListInfo boundParams);
  static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
  static void pgss_ExecutorRun(QueryDesc *queryDesc,
  				 ScanDirection direction,
***************
*** 179,186 **** static void pgss_ProcessUtility(Node *parsetree,
  					DestReceiver *dest, char *completionTag);
  static uint32 pgss_hash_fn(const void *key, Size keysize);
  static int	pgss_match_fn(const void *key1, const void *key2, Size keysize);
! static void pgss_store(const char *query, double total_time, uint64 rows,
! 		   const BufferUsage *bufusage);
  static Size pgss_memsize(void);
  static pgssEntry *entry_alloc(pgssHashKey *key);
  static void entry_dealloc(void);
--- 184,191 ----
  					DestReceiver *dest, char *completionTag);
  static uint32 pgss_hash_fn(const void *key, Size keysize);
  static int	pgss_match_fn(const void *key1, const void *key2, Size keysize);
! static void pgss_store(const char *query, double plan_time, double total_time,
! 		   uint64 rows, const BufferUsage *bufusage);
  static Size pgss_memsize(void);
  static pgssEntry *entry_alloc(pgssHashKey *key);
  static void entry_dealloc(void);
***************
*** 269,274 **** _PG_init(void)
--- 274,281 ----
  	 */
  	prev_shmem_startup_hook = shmem_startup_hook;
  	shmem_startup_hook = pgss_shmem_startup;
+ 	prev_planner_hook = planner_hook;
+ 	planner_hook = pgss_planner;
  	prev_ExecutorStart = ExecutorStart_hook;
  	ExecutorStart_hook = pgss_ExecutorStart;
  	prev_ExecutorRun = ExecutorRun_hook;
***************
*** 289,294 **** _PG_fini(void)
--- 296,302 ----
  {
  	/* Uninstall hooks. */
  	shmem_startup_hook = prev_shmem_startup_hook;
+ 	planner_hook = prev_planner_hook;
  	ExecutorStart_hook = prev_ExecutorStart;
  	ExecutorRun_hook = prev_ExecutorRun;
  	ExecutorFinish_hook = prev_ExecutorFinish;
***************
*** 505,510 **** error:
--- 513,565 ----
  }
  
  /*
+  * planner hook
+  */
+ static PlannedStmt *
+ pgss_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+ {
+ 	PlannedStmt *result;
+ 
+ 	if (pgss_enabled())
+ 	{
+ 		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);
+ 
+ 		result->plan_time = INSTR_TIME_GET_DOUBLE(duration);
+ 	}
+ 	else
+ 	{
+ 		if (prev_planner_hook)
+ 			result = prev_planner_hook(parse, cursorOptions, boundParams);
+ 		else
+ 			result = standard_planner(parse, cursorOptions, boundParams);
+ 	}
+ 
+ 	return result;
+ }
+ 
+ /*
   * ExecutorStart hook: start up tracking if needed
   */
  static void
***************
*** 594,599 **** pgss_ExecutorEnd(QueryDesc *queryDesc)
--- 649,655 ----
  		InstrEndLoop(queryDesc->totaltime);
  
  		pgss_store(queryDesc->sourceText,
+ 				   queryDesc->plannedstmt->plan_time,
  				   queryDesc->totaltime->total,
  				   queryDesc->estate->es_processed,
  				   &queryDesc->totaltime->bufusage);
***************
*** 671,677 **** pgss_ProcessUtility(Node *parsetree, const char *queryString,
  		bufusage.temp_blks_written =
  			pgBufferUsage.temp_blks_written - bufusage.temp_blks_written;
  
! 		pgss_store(queryString, INSTR_TIME_GET_DOUBLE(duration), rows,
  				   &bufusage);
  	}
  	else
--- 727,734 ----
  		bufusage.temp_blks_written =
  			pgBufferUsage.temp_blks_written - bufusage.temp_blks_written;
  
! 		/* planning time is always 0 because utility commands have no plans */
! 		pgss_store(queryString, 0, INSTR_TIME_GET_DOUBLE(duration), rows,
  				   &bufusage);
  	}
  	else
***************
*** 723,730 **** pgss_match_fn(const void *key1, const void *key2, Size keysize)
   * Store some statistics for a statement.
   */
  static void
! pgss_store(const char *query, double total_time, uint64 rows,
! 		   const BufferUsage *bufusage)
  {
  	pgssHashKey key;
  	double		usage;
--- 780,787 ----
   * Store some statistics for a statement.
   */
  static void
! pgss_store(const char *query, double plan_time, double total_time,
! 		   uint64 rows, const BufferUsage *bufusage)
  {
  	pgssHashKey key;
  	double		usage;
***************
*** 768,773 **** pgss_store(const char *query, double total_time, uint64 rows,
--- 825,831 ----
  
  		SpinLockAcquire(&e->mutex);
  		e->counters.calls += 1;
+ 		e->counters.plan_time += plan_time;
  		e->counters.total_time += total_time;
  		e->counters.rows += rows;
  		e->counters.shared_blks_hit += bufusage->shared_blks_hit;
***************
*** 802,808 **** pg_stat_statements_reset(PG_FUNCTION_ARGS)
  }
  
  #define PG_STAT_STATEMENTS_COLS_V1_0	14
! #define PG_STAT_STATEMENTS_COLS			16
  
  /*
   * Retrieve statement statistics.
--- 860,866 ----
  }
  
  #define PG_STAT_STATEMENTS_COLS_V1_0	14
! #define PG_STAT_STATEMENTS_COLS			17
  
  /*
   * Retrieve statement statistics.
***************
*** 895,900 **** pg_stat_statements(PG_FUNCTION_ARGS)
--- 953,959 ----
  		}
  
  		values[i++] = Int64GetDatumFast(tmp.calls);
+ 		values[i++] = Float8GetDatumFast(tmp.plan_time);
  		values[i++] = Float8GetDatumFast(tmp.total_time);
  		values[i++] = Int64GetDatumFast(tmp.rows);
  		values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
*** a/doc/src/sgml/pgstatstatements.sgml
--- b/doc/src/sgml/pgstatstatements.sgml
***************
*** 9,15 ****
  
   <para>
    The <filename>pg_stat_statements</filename> module provides a means for
!   tracking execution statistics of all SQL statements executed by a server.
   </para>
  
   <para>
--- 9,15 ----
  
   <para>
    The <filename>pg_stat_statements</filename> module provides a means for
!   tracking planning and execution statistics of all SQL statements executed by a server.
   </para>
  
   <para>
***************
*** 72,81 ****
       </row>
  
       <row>
        <entry><structfield>total_time</structfield></entry>
        <entry><type>double precision</type></entry>
        <entry></entry>
!       <entry>Total time spent in the statement, in seconds</entry>
       </row>
  
       <row>
--- 72,88 ----
       </row>
  
       <row>
+       <entry><structfield>plan_time</structfield></entry>
+       <entry><type>double precision</type></entry>
+       <entry></entry>
+       <entry>Total planning time spent in the statement, in seconds</entry>
+      </row>
+ 
+      <row>
        <entry><structfield>total_time</structfield></entry>
        <entry><type>double precision</type></entry>
        <entry></entry>
!       <entry>Total execution time spent in the statement, in seconds</entry>
       </row>
  
       <row>
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 93,98 **** _copyPlannedStmt(const PlannedStmt *from)
--- 93,99 ----
  	COPY_NODE_FIELD(relationOids);
  	COPY_NODE_FIELD(invalItems);
  	COPY_SCALAR_FIELD(nParamExec);
+ 	COPY_SCALAR_FIELD(plan_time);
  
  	return newnode;
  }
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 257,262 **** _outPlannedStmt(StringInfo str, const PlannedStmt *node)
--- 257,263 ----
  	WRITE_NODE_FIELD(relationOids);
  	WRITE_NODE_FIELD(invalItems);
  	WRITE_INT_FIELD(nParamExec);
+ 	WRITE_FLOAT_FIELD(plan_time, "%.0f");
  }
  
  /*
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 240,245 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 240,246 ----
  	result->relationOids = glob->relationOids;
  	result->invalItems = glob->invalItems;
  	result->nParamExec = list_length(glob->paramlist);
+ 	result->plan_time = 0;
  
  	return result;
  }
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 67,72 **** typedef struct PlannedStmt
--- 67,75 ----
  	List	   *invalItems;		/* other dependencies, as PlanInvalItems */
  
  	int			nParamExec;		/* number of PARAM_EXEC Params used */
+ 
+ 	/* This is always set 0 by the core system, but plugins can change it */
+ 	double	plan_time;	/* total time spent in planner */
  } PlannedStmt;
  
  /* macro for fetching the Plan associated with a SubPlan node */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to