1313 *
1414 *Copyright (c) 2001-2007, PostgreSQL Global Development Group
1515 *
16- *$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.152 2007/03/30 18:34:55 mha Exp $
16+ *$PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.153 2007/04/21 04:10:53 tgl Exp $
1717 * ----------
1818 */
1919#include "postgres.h"
@@ -114,6 +114,14 @@ static bool pgStatRunningInCollector = false;
114114 * Place where backends store per-table info to be sent to the collector.
115115 * We store shared relations separately from non-shared ones, to be able to
116116 * send them in separate messages.
117+ *
118+ * NOTE: once allocated, a PgStat_MsgTabstat struct belonging to a
119+ * TabStatArray is never moved or deleted for the life of the backend.
120+ * Also, we zero out the t_id fields of the contained PgStat_TableEntry
121+ * structs whenever they are not actively in use. This allows PgStat_Info
122+ * pointers to be treated as long-lived data, avoiding repeated searches in
123+ * pgstat_initstats() when a relation is repeatedly heap_open'd or
124+ * index_open'd during a transaction.
117125 */
118126typedef struct TabStatArray
119127{
@@ -169,6 +177,7 @@ static void pgstat_write_statsfile(void);
169177static HTAB * pgstat_read_statsfile (Oid onlydb );
170178static void backend_read_statsfile (void );
171179static void pgstat_read_current_status (void );
180+ static void pgstat_report_one_tabstat (TabStatArray * tsarr ,Oid dbid );
172181static HTAB * pgstat_collect_oids (Oid catalogid );
173182
174183static void pgstat_setup_memcxt (void );
@@ -606,25 +615,22 @@ void allow_immediate_pgstat_restart(void)
606615void
607616pgstat_report_tabstat (void )
608617{
609- int i ;
610-
611- if (pgStatSock < 0 ||
612- (!pgstat_collect_tuplelevel &&
613- !pgstat_collect_blocklevel ))
614- {
615- /* Not reporting stats, so just flush whatever we have */
616- RegularTabStat .tsa_used = 0 ;
617- SharedTabStat .tsa_used = 0 ;
618- return ;
619- }
620-
621618/*
622619 * For each message buffer used during the last query set the header
623- * fields and send it out.
620+ * fields and send it out; then mark the entries unused .
624621 */
625- for (i = 0 ;i < RegularTabStat .tsa_used ;i ++ )
622+ pgstat_report_one_tabstat (& RegularTabStat ,MyDatabaseId );
623+ pgstat_report_one_tabstat (& SharedTabStat ,InvalidOid );
624+ }
625+
626+ static void
627+ pgstat_report_one_tabstat (TabStatArray * tsarr ,Oid dbid )
628+ {
629+ int i ;
630+
631+ for (i = 0 ;i < tsarr -> tsa_used ;i ++ )
626632{
627- PgStat_MsgTabstat * tsmsg = RegularTabStat . tsa_messages [i ];
633+ PgStat_MsgTabstat * tsmsg = tsarr -> tsa_messages [i ];
628634int n ;
629635int len ;
630636
@@ -637,32 +643,24 @@ pgstat_report_tabstat(void)
637643pgStatXactCommit = 0 ;
638644pgStatXactRollback = 0 ;
639645
640- pgstat_setheader (& tsmsg -> m_hdr ,PGSTAT_MTYPE_TABSTAT );
641- tsmsg -> m_databaseid = MyDatabaseId ;
642- pgstat_send (tsmsg ,len );
643- }
644- RegularTabStat .tsa_used = 0 ;
645-
646- /* Ditto, for shared relations */
647- for (i = 0 ;i < SharedTabStat .tsa_used ;i ++ )
648- {
649- PgStat_MsgTabstat * tsmsg = SharedTabStat .tsa_messages [i ];
650- int n ;
651- int len ;
652-
653- n = tsmsg -> m_nentries ;
654- len = offsetof(PgStat_MsgTabstat ,m_entry [0 ])+
655- n * sizeof (PgStat_TableEntry );
656-
657- /* We don't report transaction commit/abort here */
658- tsmsg -> m_xact_commit = 0 ;
659- tsmsg -> m_xact_rollback = 0 ;
646+ /*
647+ * It's unlikely we'd get here with no socket, but maybe not
648+ * impossible
649+ */
650+ if (pgStatSock >=0 )
651+ {
652+ pgstat_setheader (& tsmsg -> m_hdr ,PGSTAT_MTYPE_TABSTAT );
653+ tsmsg -> m_databaseid = dbid ;
654+ pgstat_send (tsmsg ,len );
655+ }
660656
661- pgstat_setheader (& tsmsg -> m_hdr ,PGSTAT_MTYPE_TABSTAT );
662- tsmsg -> m_databaseid = InvalidOid ;
663- pgstat_send (tsmsg ,len );
657+ /*
658+ * Zero out the entries, to mark them unused and prepare them
659+ * for next use.
660+ */
661+ MemSet (tsmsg ,0 ,len );
664662}
665- SharedTabStat . tsa_used = 0 ;
663+ tsarr -> tsa_used = 0 ;
666664}
667665
668666
@@ -1013,7 +1011,7 @@ more_tabstat_space(TabStatArray *tsarr)
10131011
10141012newAlloc = tsarr -> tsa_alloc + TABSTAT_QUANTUM ;
10151013
1016- /* Create (another) quantum of message buffers */
1014+ /* Create (another) quantum of message buffers, and zero them */
10171015newMessages = (PgStat_MsgTabstat * )
10181016MemoryContextAllocZero (TopMemoryContext ,
10191017sizeof (PgStat_MsgTabstat )* TABSTAT_QUANTUM );
@@ -1043,6 +1041,17 @@ more_tabstat_space(TabStatArray *tsarr)
10431041 *of Relation or Scan structures. The data placed into these
10441042 *structures from here tell where later to count for buffer reads,
10451043 *scans and tuples fetched.
1044+ *
1045+ *NOTE: PgStat_Info pointers in scan structures are really redundant
1046+ *with those in relcache entries. The passed stats pointer might point
1047+ *either to the Relation struct's own pgstat_info field, or to one in
1048+ *a scan structure; we'll set the Relation pg_statinfo and copy it to
1049+ *the scan struct.
1050+ *
1051+ *We assume that a relcache entry's pgstat_info field is zeroed by
1052+ *relcache.c when the relcache entry is made; thereafter it is long-lived
1053+ *data. We can avoid repeated searches of the TabStat arrays when the
1054+ *same relation is touched repeatedly within a transaction.
10461055 * ----------
10471056 */
10481057void
@@ -1055,21 +1064,31 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
10551064int mb ;
10561065int i ;
10571066
1058- /*
1059- * Initialize data not to count at all.
1060- */
1061- stats -> tabentry = NULL ;
1062-
10631067if (pgStatSock < 0 ||
10641068!(pgstat_collect_tuplelevel ||
10651069pgstat_collect_blocklevel ))
1070+ {
1071+ /* We're not counting at all. */
1072+ stats -> tabentry = NULL ;
10661073return ;
1074+ }
10671075
1068- tsarr = rel -> rd_rel -> relisshared ?& SharedTabStat :& RegularTabStat ;
1076+ /*
1077+ * If we already set up this relation in the current transaction,
1078+ * just copy the pointer.
1079+ */
1080+ if (rel -> pgstat_info .tabentry != NULL &&
1081+ ((PgStat_TableEntry * )rel -> pgstat_info .tabentry )-> t_id == rel_id )
1082+ {
1083+ stats -> tabentry = rel -> pgstat_info .tabentry ;
1084+ return ;
1085+ }
10691086
10701087/*
10711088 * Search the already-used message slots for this relation.
10721089 */
1090+ tsarr = rel -> rd_rel -> relisshared ?& SharedTabStat :& RegularTabStat ;
1091+
10731092for (mb = 0 ;mb < tsarr -> tsa_used ;mb ++ )
10741093{
10751094tsmsg = tsarr -> tsa_messages [mb ];
@@ -1078,7 +1097,8 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
10781097{
10791098if (tsmsg -> m_entry [i ].t_id == rel_id )
10801099{
1081- stats -> tabentry = (void * )& (tsmsg -> m_entry [i ]);
1100+ rel -> pgstat_info .tabentry = (void * )& (tsmsg -> m_entry [i ]);
1101+ stats -> tabentry = rel -> pgstat_info .tabentry ;
10821102return ;
10831103}
10841104}
@@ -1088,13 +1108,14 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
10881108
10891109/*
10901110 * Not found, but found a message buffer with an empty slot instead.
1091- * Fine, let's use this one.
1111+ * Fine, let's use this one. We assume the entry was already zeroed,
1112+ * either at creation or after last use.
10921113 */
10931114i = tsmsg -> m_nentries ++ ;
10941115useent = & tsmsg -> m_entry [i ];
1095- MemSet (useent ,0 ,sizeof (PgStat_TableEntry ));
10961116useent -> t_id = rel_id ;
1097- stats -> tabentry = (void * )useent ;
1117+ rel -> pgstat_info .tabentry = (void * )useent ;
1118+ stats -> tabentry = rel -> pgstat_info .tabentry ;
10981119return ;
10991120}
11001121
@@ -1111,9 +1132,9 @@ pgstat_initstats(PgStat_Info *stats, Relation rel)
11111132tsmsg = tsarr -> tsa_messages [mb ];
11121133tsmsg -> m_nentries = 1 ;
11131134useent = & tsmsg -> m_entry [0 ];
1114- MemSet (useent ,0 ,sizeof (PgStat_TableEntry ));
11151135useent -> t_id = rel_id ;
1116- stats -> tabentry = (void * )useent ;
1136+ rel -> pgstat_info .tabentry = (void * )useent ;
1137+ stats -> tabentry = rel -> pgstat_info .tabentry ;
11171138}
11181139
11191140