Here's v0.5. (Why did you use that weird decimal versioning scheme?  You
could just say "v4" and save a couple of keystrokes).  This patch makes
perfect sense to me now.  I was ready to commit, but I checked the
regression test you added and noticed that you're only reading results
for the last set of operations because they all use the same table and
so each new set clobbers the values for the previous one.  So I modified
them to use one table for each set, and report the counters for all
tables.  In doing this I noticed that the one for trunc_stats_test3 is
at odds with what your comment in the .sql file says; would you review
it please?  Thanks.

(I didn't update the expected file.)

BTW you forgot to update expected/prepared_xact_1.out, for the case when
prep xacts are disabled.

If some other committer decides to give this a go, please remember to
bump catversion before pushing.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66d5083..b2993b8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -71,6 +71,7 @@
 #include "parser/parse_type.h"
 #include "parser/parse_utilcmd.h"
 #include "parser/parser.h"
+#include "pgstat.h"
 #include "rewrite/rewriteDefine.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
@@ -1220,6 +1221,8 @@ ExecuteTruncate(TruncateStmt *stmt)
 			 */
 			reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST);
 		}
+
+		pgstat_count_truncate(rel);
 	}
 
 	/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 827ad71..afe1da2 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -197,8 +197,12 @@ typedef struct TwoPhasePgStatRecord
 	PgStat_Counter tuples_inserted;		/* tuples inserted in xact */
 	PgStat_Counter tuples_updated;		/* tuples updated in xact */
 	PgStat_Counter tuples_deleted;		/* tuples deleted in xact */
+	PgStat_Counter inserted_pre_trunc;	/* tuples inserted prior to truncate */
+	PgStat_Counter updated_pre_trunc;	/* tuples updated prior to truncate */
+	PgStat_Counter deleted_pre_trunc;	/* tuples deleted prior to truncate */
 	Oid			t_id;			/* table's OID */
 	bool		t_shared;		/* is it a shared catalog? */
+	bool		t_truncated;	/* was the relation truncated? */
 } TwoPhasePgStatRecord;
 
 /*
@@ -1859,6 +1863,59 @@ pgstat_count_heap_delete(Relation rel)
 }
 
 /*
+ * pgstat_table_record_truncate - remember i/u/d counts accumulated so far
+ */
+static void
+pgstat_table_record_truncate(PgStat_TableXactStatus *trans)
+{
+	if (!trans->truncated)
+	{
+		trans->inserted_pre_trunc = trans->tuples_inserted;
+		trans->updated_pre_trunc = trans->tuples_updated;
+		trans->deleted_pre_trunc = trans->tuples_deleted;
+	}
+}
+
+/*
+ * pgstat_table_restore_truncate - restore counters when a truncate aborts
+ */
+static void
+pgstat_table_restore_truncate(PgStat_TableXactStatus *trans)
+{
+	if (trans->truncated)
+	{
+		trans->tuples_inserted = trans->inserted_pre_trunc;
+		trans->tuples_updated = trans->updated_pre_trunc;
+		trans->tuples_deleted = trans->deleted_pre_trunc;
+	}
+}
+
+/*
+ * pgstat_count_truncate - update tuple counters due to truncate
+ */
+void
+pgstat_count_truncate(Relation rel)
+{
+	PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+
+	if (pgstat_info != NULL)
+	{
+		/* We have to log the effect at the proper transactional level */
+		int			nest_level = GetCurrentTransactionNestLevel();
+
+		if (pgstat_info->trans == NULL ||
+			pgstat_info->trans->nest_level != nest_level)
+			add_tabstat_xact_level(pgstat_info, nest_level);
+
+		pgstat_table_record_truncate(pgstat_info->trans);
+		pgstat_info->trans->truncated = true;
+		pgstat_info->trans->tuples_inserted = 0;
+		pgstat_info->trans->tuples_updated = 0;
+		pgstat_info->trans->tuples_deleted = 0;
+	}
+}
+
+/*
  * pgstat_update_heap_dead_tuples - update dead-tuples count
  *
  * The semantics of this are that we are reporting the nontransactional
@@ -1916,12 +1973,22 @@ AtEOXact_PgStat(bool isCommit)
 			Assert(trans->upper == NULL);
 			tabstat = trans->parent;
 			Assert(tabstat->trans == trans);
+			/* restore pre-truncate stats (if any) in case of aborted xact */
+			if (!isCommit)
+				pgstat_table_restore_truncate(trans);
 			/* count attempted actions regardless of commit/abort */
 			tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
 			tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
 			tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
 			if (isCommit)
 			{
+				tabstat->t_counts.t_truncated = trans->truncated;
+				if (trans->truncated)
+				{
+					/* forget live/dead stats seen by backend thus far */
+					tabstat->t_counts.t_delta_live_tuples = 0;
+					tabstat->t_counts.t_delta_dead_tuples = 0;
+				}
 				/* insert adds a live tuple, delete removes one */
 				tabstat->t_counts.t_delta_live_tuples +=
 					trans->tuples_inserted - trans->tuples_deleted;
@@ -1986,9 +2053,21 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 			{
 				if (trans->upper && trans->upper->nest_level == nestDepth - 1)
 				{
-					trans->upper->tuples_inserted += trans->tuples_inserted;
-					trans->upper->tuples_updated += trans->tuples_updated;
-					trans->upper->tuples_deleted += trans->tuples_deleted;
+					if (trans->truncated)
+					{
+						pgstat_table_record_truncate(trans->upper);
+						trans->upper->truncated = true;
+						/* replace upper xact stats with ours */
+						trans->upper->tuples_inserted = trans->tuples_inserted;
+						trans->upper->tuples_updated = trans->tuples_updated;
+						trans->upper->tuples_deleted = trans->tuples_deleted;
+					}
+					else
+					{
+						trans->upper->tuples_inserted += trans->tuples_inserted;
+						trans->upper->tuples_updated += trans->tuples_updated;
+						trans->upper->tuples_deleted += trans->tuples_deleted;
+					}
 					tabstat->trans = trans->upper;
 					pfree(trans);
 				}
@@ -2017,6 +2096,8 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 				 * subtransaction
 				 */
 
+				/* first restore values obliterated by truncate */
+				pgstat_table_restore_truncate(trans);
 				/* count attempted actions regardless of commit/abort */
 				tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
 				tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
@@ -2065,8 +2146,12 @@ AtPrepare_PgStat(void)
 			record.tuples_inserted = trans->tuples_inserted;
 			record.tuples_updated = trans->tuples_updated;
 			record.tuples_deleted = trans->tuples_deleted;
+			record.inserted_pre_trunc = trans->inserted_pre_trunc;
+			record.updated_pre_trunc = trans->updated_pre_trunc;
+			record.deleted_pre_trunc = trans->deleted_pre_trunc;
 			record.t_id = tabstat->t_id;
 			record.t_shared = tabstat->t_shared;
+			record.t_truncated = trans->truncated;
 
 			RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
 								   &record, sizeof(TwoPhasePgStatRecord));
@@ -2132,6 +2217,8 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
 	pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
 	pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
 	pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+	pgstat_info->t_counts.t_truncated = rec->t_truncated;
+
 	pgstat_info->t_counts.t_delta_live_tuples +=
 		rec->tuples_inserted - rec->tuples_deleted;
 	pgstat_info->t_counts.t_delta_dead_tuples +=
@@ -2158,6 +2245,12 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
 	pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
 
 	/* Same math as in AtEOXact_PgStat, abort case */
+	if (rec->t_truncated)
+	{
+		rec->tuples_inserted = rec->inserted_pre_trunc;
+		rec->tuples_updated = rec->updated_pre_trunc;
+		rec->tuples_deleted = rec->deleted_pre_trunc;
+	}
 	pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
 	pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
 	pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
@@ -4658,6 +4751,11 @@ pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
 			tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated;
 			tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
 			tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated;
+			if (tabmsg->t_counts.t_truncated)
+			{
+				tabentry->n_live_tuples = 0;
+				tabentry->n_dead_tuples = 0;
+			}
 			tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
 			tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
 			tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 416769a..6356085 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -104,6 +104,7 @@ typedef struct PgStat_TableCounts
 	PgStat_Counter t_tuples_updated;
 	PgStat_Counter t_tuples_deleted;
 	PgStat_Counter t_tuples_hot_updated;
+	bool		t_truncated;
 
 	PgStat_Counter t_delta_live_tuples;
 	PgStat_Counter t_delta_dead_tuples;
@@ -165,6 +166,10 @@ typedef struct PgStat_TableXactStatus
 	PgStat_Counter tuples_inserted;		/* tuples inserted in (sub)xact */
 	PgStat_Counter tuples_updated;		/* tuples updated in (sub)xact */
 	PgStat_Counter tuples_deleted;		/* tuples deleted in (sub)xact */
+	bool		truncated;		/* relation truncated in this (sub)xact */
+	PgStat_Counter inserted_pre_trunc;	/* tuples inserted prior to truncate */
+	PgStat_Counter updated_pre_trunc;	/* tuples updated prior to truncate */
+	PgStat_Counter deleted_pre_trunc;	/* tuples deleted prior to truncate */
 	int			nest_level;		/* subtransaction nest level */
 	/* links to other structs for same relation: */
 	struct PgStat_TableXactStatus *upper;		/* next higher subxact if any */
@@ -960,6 +965,7 @@ extern void pgstat_initstats(Relation rel);
 extern void pgstat_count_heap_insert(Relation rel, int n);
 extern void pgstat_count_heap_update(Relation rel, bool hot);
 extern void pgstat_count_heap_delete(Relation rel);
+extern void pgstat_count_truncate(Relation rel);
 extern void pgstat_update_heap_dead_tuples(Relation rel, int delta);
 
 extern void pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
diff --git a/src/test/regress/expected/prepared_xacts.out b/src/test/regress/expected/prepared_xacts.out
index c0b0864..9dbf874 100644
--- a/src/test/regress/expected/prepared_xacts.out
+++ b/src/test/regress/expected/prepared_xacts.out
@@ -247,8 +247,58 @@ SELECT gid FROM pg_prepared_xacts;
 -----
 (0 rows)
 
+CREATE TABLE pxtest5 (a SERIAL);
+INSERT INTO pxtest5 DEFAULT VALUES;
+SELECT * FROM pxtest5;
+ a 
+---
+ 1
+(1 row)
+
+BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  TRUNCATE pxtest5;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+PREPARE TRANSACTION 'trunc-and-pgstat';
+SELECT pg_sleep(0.5);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+ n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
+-----------+-----------+-----------+------------+------------
+         1 |         0 |         0 |          1 |          0
+(1 row)
+
+COMMIT PREPARED 'trunc-and-pgstat';
+SELECT pg_sleep(0.5);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+ n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
+-----------+-----------+-----------+------------+------------
+         2 |         0 |         0 |          1 |          0
+(1 row)
+
+SELECT * FROM pxtest5;
+ a 
+---
+ 4
+(1 row)
+
 -- Clean up
 DROP TABLE pxtest2;
 DROP TABLE pxtest3;  -- will still be there if prepared xacts are disabled
 ERROR:  table "pxtest3" does not exist
 DROP TABLE pxtest4;
+DROP TABLE pxtest5;
diff --git a/src/test/regress/expected/prepared_xacts_1.out b/src/test/regress/expected/prepared_xacts_1.out
index 898f278..dfdd5eb 100644
--- a/src/test/regress/expected/prepared_xacts_1.out
+++ b/src/test/regress/expected/prepared_xacts_1.out
@@ -249,9 +249,62 @@ SELECT gid FROM pg_prepared_xacts;
 -----
 (0 rows)
 
+CREATE TABLE pxtest5 (a SERIAL);
+INSERT INTO pxtest5 DEFAULT VALUES;
+SELECT * FROM pxtest5;
+ a 
+---
+ 1
+(1 row)
+
+BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  TRUNCATE pxtest5;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+PREPARE TRANSACTION 'trunc-and-pgstat';
+ERROR:  prepared transactions are disabled
+HINT:  Set max_prepared_transactions to a nonzero value.
+SELECT pg_sleep(0.5);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+ n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
+-----------+-----------+-----------+------------+------------
+         3 |         0 |         0 |          1 |          2
+(1 row)
+
+COMMIT PREPARED 'trunc-and-pgstat';
+ERROR:  prepared transaction with identifier "trunc-and-pgstat" does not exist
+SELECT pg_sleep(0.5);
+ pg_sleep 
+----------
+ 
+(1 row)
+
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+ n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
+-----------+-----------+-----------+------------+------------
+         3 |         0 |         0 |          1 |          2
+(1 row)
+
+SELECT * FROM pxtest5;
+ a 
+---
+ 1
+(1 row)
+
 -- Clean up
 DROP TABLE pxtest2;
 ERROR:  table "pxtest2" does not exist
 DROP TABLE pxtest3;  -- will still be there if prepared xacts are disabled
 DROP TABLE pxtest4;
 ERROR:  table "pxtest4" does not exist
+DROP TABLE pxtest5;
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index ec0ff65..7dac69b 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -56,11 +56,65 @@ begin
 
   end loop;
 
+  if not updated then
+    raise warning 'stats update never happened';
+  end if;
+
   -- report time waited in postmaster log (where it won't change test output)
   raise log 'wait_for_stats delayed % seconds',
     extract(epoch from clock_timestamp() - start_time);
 end
 $$ language plpgsql;
+-- to test effects of TRUNCATE on n_live_tup/n_dead_tup counters
+CREATE TABLE trunc_stats_test(id serial);
+-- populate the table so we can check that n_live_tup is reset to 0
+-- after truncate
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+TRUNCATE trunc_stats_test;
+-- repopulate the table
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+UPDATE trunc_stats_test SET id = id + 10 WHERE id < 6; -- UPDATE 2
+DELETE FROM trunc_stats_test WHERE id = 6;             -- DELETE 1
+BEGIN;
+UPDATE trunc_stats_test SET id = id + 100; -- UPDATE 2
+TRUNCATE trunc_stats_test;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+COMMIT;
+-- now to use a savepoint: this should only count 1 insert and have 1
+-- live tuple after commit
+BEGIN;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+SAVEPOINT p1;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+TRUNCATE trunc_stats_test;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+RELEASE SAVEPOINT p1;
+COMMIT;
+-- rollback a savepoint: this should only count 3 inserts and have 3
+-- live tuples after commit (and 1 dead one due to aborted subxact)
+BEGIN;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+SAVEPOINT p1;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+TRUNCATE trunc_stats_test;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+ROLLBACK TO SAVEPOINT p1;
+COMMIT;
+-- rollback a truncate: this should count 2 inserts and produce 2 more
+-- dead tuples
+BEGIN;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+TRUNCATE trunc_stats_test;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+ROLLBACK;
 -- do a seqscan
 SELECT count(*) FROM tenk2;
  count 
@@ -91,6 +145,14 @@ SELECT wait_for_stats();
 (1 row)
 
 -- check effects
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='trunc_stats_test';
+ n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
+-----------+-----------+-----------+------------+------------
+        14 |         2 |         1 |          3 |          4
+(1 row)
+
 SELECT st.seq_scan >= pr.seq_scan + 1,
        st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
        st.idx_scan >= pr.idx_scan + 1,
@@ -111,4 +173,5 @@ SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
  t        | t
 (1 row)
 
+DROP TABLE trunc_stats_test;
 -- End of Stats Test
diff --git a/src/test/regress/sql/prepared_xacts.sql b/src/test/regress/sql/prepared_xacts.sql
index 7902152..56d5857 100644
--- a/src/test/regress/sql/prepared_xacts.sql
+++ b/src/test/regress/sql/prepared_xacts.sql
@@ -152,7 +152,34 @@ SELECT * FROM pxtest3;
 -- There should be no prepared transactions
 SELECT gid FROM pg_prepared_xacts;
 
+CREATE TABLE pxtest5 (a SERIAL);
+INSERT INTO pxtest5 DEFAULT VALUES;
+
+SELECT * FROM pxtest5;
+
+BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+  TRUNCATE pxtest5;
+  INSERT INTO pxtest5 DEFAULT VALUES;
+PREPARE TRANSACTION 'trunc-and-pgstat';
+
+SELECT pg_sleep(0.5);
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+
+COMMIT PREPARED 'trunc-and-pgstat';
+
+SELECT pg_sleep(0.5);
+SELECT n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname='pxtest5';
+
+SELECT * FROM pxtest5;
+
 -- Clean up
 DROP TABLE pxtest2;
 DROP TABLE pxtest3;  -- will still be there if prepared xacts are disabled
 DROP TABLE pxtest4;
+DROP TABLE pxtest5;
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 646b9ac..60bb442 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -51,12 +51,78 @@ begin
 
   end loop;
 
+  if not updated then
+    raise warning 'stats update never happened';
+  end if;
+
   -- report time waited in postmaster log (where it won't change test output)
   raise log 'wait_for_stats delayed % seconds',
     extract(epoch from clock_timestamp() - start_time);
 end
 $$ language plpgsql;
 
+-- to test effects of TRUNCATE on n_live_tup/n_dead_tup counters
+CREATE TABLE trunc_stats_test(id serial);
+CREATE TABLE trunc_stats_test1(id serial);
+CREATE TABLE trunc_stats_test2(id serial);
+CREATE TABLE trunc_stats_test3(id serial);
+CREATE TABLE trunc_stats_test4(id serial);
+
+-- populate the table so we can check that n_live_tup is reset to 0
+-- after truncate
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+INSERT INTO trunc_stats_test DEFAULT VALUES;
+
+TRUNCATE trunc_stats_test;
+
+-- repopulate the table
+INSERT INTO trunc_stats_test1 DEFAULT VALUES;
+INSERT INTO trunc_stats_test1 DEFAULT VALUES;
+INSERT INTO trunc_stats_test1 DEFAULT VALUES;
+UPDATE trunc_stats_test1 SET id = id + 10 WHERE id < 6; -- UPDATE 2
+DELETE FROM trunc_stats_test1 WHERE id = 6;             -- DELETE 1
+
+BEGIN;
+UPDATE trunc_stats_test1 SET id = id + 100; -- UPDATE 2
+TRUNCATE trunc_stats_test1;
+INSERT INTO trunc_stats_test1 DEFAULT VALUES;
+COMMIT;
+
+-- now to use a savepoint: this should only count 1 insert and have 1
+-- live tuple after commit
+BEGIN;
+INSERT INTO trunc_stats_test2 DEFAULT VALUES;
+INSERT INTO trunc_stats_test2 DEFAULT VALUES;
+SAVEPOINT p1;
+INSERT INTO trunc_stats_test2 DEFAULT VALUES;
+TRUNCATE trunc_stats_test2;
+INSERT INTO trunc_stats_test2 DEFAULT VALUES;
+RELEASE SAVEPOINT p1;
+COMMIT;
+
+-- rollback a savepoint: this should only count 3 inserts and have 3
+-- live tuples after commit (and 1 dead one due to aborted subxact)
+BEGIN;
+INSERT INTO trunc_stats_test3 DEFAULT VALUES;
+INSERT INTO trunc_stats_test3 DEFAULT VALUES;
+SAVEPOINT p1;
+INSERT INTO trunc_stats_test3 DEFAULT VALUES;
+INSERT INTO trunc_stats_test3 DEFAULT VALUES;
+TRUNCATE trunc_stats_test3;
+INSERT INTO trunc_stats_test3 DEFAULT VALUES;
+ROLLBACK TO SAVEPOINT p1;
+COMMIT;
+
+-- rollback a truncate: this should count 2 inserts and produce 2 more
+-- dead tuples
+BEGIN;
+INSERT INTO trunc_stats_test4 DEFAULT VALUES;
+INSERT INTO trunc_stats_test4 DEFAULT VALUES;
+TRUNCATE trunc_stats_test4;
+INSERT INTO trunc_stats_test4 DEFAULT VALUES;
+ROLLBACK;
+
 -- do a seqscan
 SELECT count(*) FROM tenk2;
 -- do an indexscan
@@ -70,15 +136,21 @@ SELECT pg_sleep(1.0);
 SELECT wait_for_stats();
 
 -- check effects
+SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
+  FROM pg_stat_user_tables
+ WHERE relname like 'trunc_stats_test%' order by relname;
+
 SELECT st.seq_scan >= pr.seq_scan + 1,
        st.seq_tup_read >= pr.seq_tup_read + cl.reltuples,
        st.idx_scan >= pr.idx_scan + 1,
        st.idx_tup_fetch >= pr.idx_tup_fetch + 1
   FROM pg_stat_user_tables AS st, pg_class AS cl, prevstats AS pr
  WHERE st.relname='tenk2' AND cl.relname='tenk2';
+
 SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
        st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1
   FROM pg_statio_user_tables AS st, pg_class AS cl, prevstats AS pr
  WHERE st.relname='tenk2' AND cl.relname='tenk2';
 
+DROP TABLE trunc_stats_test;
 -- End of Stats Test
-- 
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