@@ -312,6 +312,11 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
312312Oid save_userid ;
313313int save_sec_context ;
314314int save_nestlevel ;
315+ int64 AnalyzePageHit = VacuumPageHit ;
316+ int64 AnalyzePageMiss = VacuumPageMiss ;
317+ int64 AnalyzePageDirty = VacuumPageDirty ;
318+ PgStat_Counter startreadtime = 0 ;
319+ PgStat_Counter startwritetime = 0 ;
315320
316321if (inh )
317322ereport (elevel ,
@@ -346,8 +351,14 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
346351/* measure elapsed time iff autovacuum logging requires it */
347352if (IsAutoVacuumWorkerProcess ()&& params -> log_min_duration >=0 )
348353{
354+ if (track_io_timing )
355+ {
356+ startreadtime = pgStatBlockReadTime ;
357+ startwritetime = pgStatBlockWriteTime ;
358+ }
359+
349360pg_rusage_init (& ru0 );
350- if (params -> log_min_duration > 0 )
361+ if (params -> log_min_duration >= 0 )
351362starttime = GetCurrentTimestamp ();
352363}
353364
@@ -692,15 +703,90 @@ do_analyze_rel(Relation onerel, VacuumParams *params,
692703/* Log the action if appropriate */
693704if (IsAutoVacuumWorkerProcess ()&& params -> log_min_duration >=0 )
694705{
706+ TimestampTz endtime = GetCurrentTimestamp ();
707+
695708if (params -> log_min_duration == 0 ||
696- TimestampDifferenceExceeds (starttime ,GetCurrentTimestamp () ,
709+ TimestampDifferenceExceeds (starttime ,endtime ,
697710params -> log_min_duration ))
711+ {
712+ long delay_in_ms ;
713+ double read_rate = 0 ;
714+ double write_rate = 0 ;
715+ StringInfoData buf ;
716+
717+ /*
718+ * Calculate the difference in the Page Hit/Miss/Dirty that
719+ * happened as part of the analyze by subtracting out the
720+ * pre-analyze values which we saved above.
721+ */
722+ AnalyzePageHit = VacuumPageHit - AnalyzePageHit ;
723+ AnalyzePageMiss = VacuumPageMiss - AnalyzePageMiss ;
724+ AnalyzePageDirty = VacuumPageDirty - AnalyzePageDirty ;
725+
726+ /*
727+ * We do not expect an analyze to take > 25 days and it simplifies
728+ * things a bit to use TimestampDifferenceMilliseconds.
729+ */
730+ delay_in_ms = TimestampDifferenceMilliseconds (starttime ,endtime );
731+
732+ /*
733+ * Note that we are reporting these read/write rates in the same
734+ * manner as VACUUM does, which means that while the 'average read
735+ * rate' here actually corresponds to page misses and resulting
736+ * reads which are also picked up by track_io_timing, if enabled,
737+ * the 'average write rate' is actually talking about the rate of
738+ * pages being dirtied, not being written out, so it's typical to
739+ * have a non-zero 'avg write rate' while I/O Timings only reports
740+ * reads.
741+ *
742+ * It's not clear that an ANALYZE will ever result in
743+ * FlushBuffer() being called, but we track and support reporting
744+ * on I/O write time in case that changes as it's practically free
745+ * to do so anyway.
746+ */
747+
748+ if (delay_in_ms > 0 )
749+ {
750+ read_rate = (double )BLCKSZ * AnalyzePageMiss / (1024 * 1024 ) /
751+ (delay_in_ms /1000.0 );
752+ write_rate = (double )BLCKSZ * AnalyzePageDirty / (1024 * 1024 ) /
753+ (delay_in_ms /1000.0 );
754+ }
755+
756+ /*
757+ * We split this up so we don't emit empty I/O timing values when
758+ * track_io_timing isn't enabled.
759+ */
760+
761+ initStringInfo (& buf );
762+ appendStringInfo (& buf ,_ ("automatic analyze of table \"%s.%s.%s\"\n" ),
763+ get_database_name (MyDatabaseId ),
764+ get_namespace_name (RelationGetNamespace (onerel )),
765+ RelationGetRelationName (onerel ));
766+ appendStringInfo (& buf ,_ ("buffer usage: %lld hits, %lld misses, %lld dirtied\n" ),
767+ (long long )AnalyzePageHit ,
768+ (long long )AnalyzePageMiss ,
769+ (long long )AnalyzePageDirty );
770+ appendStringInfo (& buf ,_ ("avg read rate: %.3f MB/s, avg write rate: %.3f MB/s\n" ),
771+ read_rate ,write_rate );
772+ if (track_io_timing )
773+ {
774+ appendStringInfoString (& buf ,_ ("I/O Timings:" ));
775+ if (pgStatBlockReadTime - startreadtime > 0 )
776+ appendStringInfo (& buf ,_ (" read=%.3f" ),
777+ (double ) (pgStatBlockReadTime - startreadtime ) /1000 );
778+ if (pgStatBlockWriteTime - startwritetime > 0 )
779+ appendStringInfo (& buf ,_ (" write=%.3f" ),
780+ (double ) (pgStatBlockWriteTime - startwritetime ) /1000 );
781+ appendStringInfoChar (& buf ,'\n' );
782+ }
783+ appendStringInfo (& buf ,_ ("system usage: %s" ),pg_rusage_show (& ru0 ));
784+
698785ereport (LOG ,
699- (errmsg ("automatic analyze of table \"%s.%s.%s\" system usage: %s" ,
700- get_database_name (MyDatabaseId ),
701- get_namespace_name (RelationGetNamespace (onerel )),
702- RelationGetRelationName (onerel ),
703- pg_rusage_show (& ru0 ))));
786+ (errmsg_internal ("%s" ,buf .data )));
787+
788+ pfree (buf .data );
789+ }
704790}
705791
706792/* Roll back any GUC changes executed by index functions */