@@ -197,8 +197,12 @@ typedef struct TwoPhasePgStatRecord
197197 PgStat_Counter tuples_inserted ; /* tuples inserted in xact */
198198 PgStat_Counter tuples_updated ; /* tuples updated in xact */
199199 PgStat_Counter tuples_deleted ; /* tuples deleted in xact */
200+ PgStat_Counter inserted_pre_trunc ; /* tuples inserted prior to truncate */
201+ PgStat_Counter updated_pre_trunc ; /* tuples updated prior to truncate */
202+ PgStat_Counter deleted_pre_trunc ; /* tuples deleted prior to truncate */
200203 Oid t_id ; /* table's OID */
201204 bool t_shared ; /* is it a shared catalog? */
205+ bool t_truncated ; /* was the relation truncated? */
202206} TwoPhasePgStatRecord ;
203207
204208/*
@@ -1858,6 +1862,64 @@ pgstat_count_heap_delete(Relation rel)
18581862 }
18591863}
18601864
1865+ /*
1866+ * pgstat_truncate_save_counters
1867+ *
1868+ * Whenever a table is truncated, we save its i/u/d counters so that they can
1869+ * be cleared, and if the (sub)xact that executed the truncate later aborts,
1870+ * the counters can be restored to the saved (pre-truncate) values. Note we do
1871+ * this on the first truncate in any particular subxact level only.
1872+ */
1873+ static void
1874+ pgstat_truncate_save_counters (PgStat_TableXactStatus * trans )
1875+ {
1876+ if (!trans -> truncated )
1877+ {
1878+ trans -> inserted_pre_trunc = trans -> tuples_inserted ;
1879+ trans -> updated_pre_trunc = trans -> tuples_updated ;
1880+ trans -> deleted_pre_trunc = trans -> tuples_deleted ;
1881+ trans -> truncated = true;
1882+ }
1883+ }
1884+
1885+ /*
1886+ * pgstat_truncate_restore_counters - restore counters when a truncate aborts
1887+ */
1888+ static void
1889+ pgstat_truncate_restore_counters (PgStat_TableXactStatus * trans )
1890+ {
1891+ if (trans -> truncated )
1892+ {
1893+ trans -> tuples_inserted = trans -> inserted_pre_trunc ;
1894+ trans -> tuples_updated = trans -> updated_pre_trunc ;
1895+ trans -> tuples_deleted = trans -> deleted_pre_trunc ;
1896+ }
1897+ }
1898+
1899+ /*
1900+ * pgstat_count_truncate - update tuple counters due to truncate
1901+ */
1902+ void
1903+ pgstat_count_truncate (Relation rel )
1904+ {
1905+ PgStat_TableStatus * pgstat_info = rel -> pgstat_info ;
1906+
1907+ if (pgstat_info != NULL )
1908+ {
1909+ /* We have to log the effect at the proper transactional level */
1910+ int nest_level = GetCurrentTransactionNestLevel ();
1911+
1912+ if (pgstat_info -> trans == NULL ||
1913+ pgstat_info -> trans -> nest_level != nest_level )
1914+ add_tabstat_xact_level (pgstat_info , nest_level );
1915+
1916+ pgstat_truncate_save_counters (pgstat_info -> trans );
1917+ pgstat_info -> trans -> tuples_inserted = 0 ;
1918+ pgstat_info -> trans -> tuples_updated = 0 ;
1919+ pgstat_info -> trans -> tuples_deleted = 0 ;
1920+ }
1921+ }
1922+
18611923/*
18621924 * pgstat_update_heap_dead_tuples - update dead-tuples count
18631925 *
@@ -1916,12 +1978,22 @@ AtEOXact_PgStat(bool isCommit)
19161978 Assert (trans -> upper == NULL );
19171979 tabstat = trans -> parent ;
19181980 Assert (tabstat -> trans == trans );
1981+ /* restore pre-truncate stats (if any) in case of aborted xact */
1982+ if (!isCommit )
1983+ pgstat_truncate_restore_counters (trans );
19191984 /* count attempted actions regardless of commit/abort */
19201985 tabstat -> t_counts .t_tuples_inserted += trans -> tuples_inserted ;
19211986 tabstat -> t_counts .t_tuples_updated += trans -> tuples_updated ;
19221987 tabstat -> t_counts .t_tuples_deleted += trans -> tuples_deleted ;
19231988 if (isCommit )
19241989 {
1990+ tabstat -> t_counts .t_truncated = trans -> truncated ;
1991+ if (trans -> truncated )
1992+ {
1993+ /* forget live/dead stats seen by backend thus far */
1994+ tabstat -> t_counts .t_delta_live_tuples = 0 ;
1995+ tabstat -> t_counts .t_delta_dead_tuples = 0 ;
1996+ }
19251997 /* insert adds a live tuple, delete removes one */
19261998 tabstat -> t_counts .t_delta_live_tuples +=
19271999 trans -> tuples_inserted - trans -> tuples_deleted ;
@@ -1986,9 +2058,21 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
19862058 {
19872059 if (trans -> upper && trans -> upper -> nest_level == nestDepth - 1 )
19882060 {
1989- trans -> upper -> tuples_inserted += trans -> tuples_inserted ;
1990- trans -> upper -> tuples_updated += trans -> tuples_updated ;
1991- trans -> upper -> tuples_deleted += trans -> tuples_deleted ;
2061+ if (trans -> truncated )
2062+ {
2063+ /* propagate the truncate status one level up */
2064+ pgstat_truncate_save_counters (trans -> upper );
2065+ /* replace upper xact stats with ours */
2066+ trans -> upper -> tuples_inserted = trans -> tuples_inserted ;
2067+ trans -> upper -> tuples_updated = trans -> tuples_updated ;
2068+ trans -> upper -> tuples_deleted = trans -> tuples_deleted ;
2069+ }
2070+ else
2071+ {
2072+ trans -> upper -> tuples_inserted += trans -> tuples_inserted ;
2073+ trans -> upper -> tuples_updated += trans -> tuples_updated ;
2074+ trans -> upper -> tuples_deleted += trans -> tuples_deleted ;
2075+ }
19922076 tabstat -> trans = trans -> upper ;
19932077 pfree (trans );
19942078 }
@@ -2017,6 +2101,8 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
20172101 * subtransaction
20182102 */
20192103
2104+ /* first restore values obliterated by truncate */
2105+ pgstat_truncate_restore_counters (trans );
20202106 /* count attempted actions regardless of commit/abort */
20212107 tabstat -> t_counts .t_tuples_inserted += trans -> tuples_inserted ;
20222108 tabstat -> t_counts .t_tuples_updated += trans -> tuples_updated ;
@@ -2065,8 +2151,12 @@ AtPrepare_PgStat(void)
20652151 record .tuples_inserted = trans -> tuples_inserted ;
20662152 record .tuples_updated = trans -> tuples_updated ;
20672153 record .tuples_deleted = trans -> tuples_deleted ;
2154+ record .inserted_pre_trunc = trans -> inserted_pre_trunc ;
2155+ record .updated_pre_trunc = trans -> updated_pre_trunc ;
2156+ record .deleted_pre_trunc = trans -> deleted_pre_trunc ;
20682157 record .t_id = tabstat -> t_id ;
20692158 record .t_shared = tabstat -> t_shared ;
2159+ record .t_truncated = trans -> truncated ;
20702160
20712161 RegisterTwoPhaseRecord (TWOPHASE_RM_PGSTAT_ID , 0 ,
20722162 & record , sizeof (TwoPhasePgStatRecord ));
@@ -2132,6 +2222,8 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
21322222 pgstat_info -> t_counts .t_tuples_inserted += rec -> tuples_inserted ;
21332223 pgstat_info -> t_counts .t_tuples_updated += rec -> tuples_updated ;
21342224 pgstat_info -> t_counts .t_tuples_deleted += rec -> tuples_deleted ;
2225+ pgstat_info -> t_counts .t_truncated = rec -> t_truncated ;
2226+
21352227 pgstat_info -> t_counts .t_delta_live_tuples +=
21362228 rec -> tuples_inserted - rec -> tuples_deleted ;
21372229 pgstat_info -> t_counts .t_delta_dead_tuples +=
@@ -2158,6 +2250,12 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
21582250 pgstat_info = get_tabstat_entry (rec -> t_id , rec -> t_shared );
21592251
21602252 /* Same math as in AtEOXact_PgStat, abort case */
2253+ if (rec -> t_truncated )
2254+ {
2255+ rec -> tuples_inserted = rec -> inserted_pre_trunc ;
2256+ rec -> tuples_updated = rec -> updated_pre_trunc ;
2257+ rec -> tuples_deleted = rec -> deleted_pre_trunc ;
2258+ }
21612259 pgstat_info -> t_counts .t_tuples_inserted += rec -> tuples_inserted ;
21622260 pgstat_info -> t_counts .t_tuples_updated += rec -> tuples_updated ;
21632261 pgstat_info -> t_counts .t_tuples_deleted += rec -> tuples_deleted ;
@@ -4658,6 +4756,12 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
46584756 tabentry -> tuples_updated += tabmsg -> t_counts .t_tuples_updated ;
46594757 tabentry -> tuples_deleted += tabmsg -> t_counts .t_tuples_deleted ;
46604758 tabentry -> tuples_hot_updated += tabmsg -> t_counts .t_tuples_hot_updated ;
4759+ /* If table was truncated, first reset the live/dead counters */
4760+ if (tabmsg -> t_counts .t_truncated )
4761+ {
4762+ tabentry -> n_live_tuples = 0 ;
4763+ tabentry -> n_dead_tuples = 0 ;
4764+ }
46614765 tabentry -> n_live_tuples += tabmsg -> t_counts .t_delta_live_tuples ;
46624766 tabentry -> n_dead_tuples += tabmsg -> t_counts .t_delta_dead_tuples ;
46634767 tabentry -> changes_since_analyze += tabmsg -> t_counts .t_changed_tuples ;
0 commit comments