Alvaro Herrera <alvhe...@2ndquadrant.com> writes:
>
>> Even with the current approach of checking the stats after every
>> isolated case it's sometimes takes quite a little more than a glance
>> to verify correctness due to side-effects of rollback (ins/upd/del
>> counters are still updated), and the way stats are affecting the dead
>> tuples counter.
>
> Honestly I think pg_regress is not the right tool to test stat counter
> updates.  It kind-of works today, but only because we don't stress it
> too much.  If you want to create a new test framework for pgstats, and
> include some tests for truncate, be my guest.

OK, I think I have now all bases covered, though the updated patch is
not that "pretty".

The problem is that we don't know in advance if the (sub)transaction is
going to succeed or abort, and in case of aborted truncate we need to
use the stats gathered prior to truncate.  Thus the need to track
insert/update/deletes that happened before first truncate separately.

To the point of making a dedicated pgstat testing tool: let's have
another TODO item?

--
Alex

>From 0b3161191a3ddb999cd9d0da08e1b6088ce07a84 Mon Sep 17 00:00:00 2001
From: Alex Shulgin <a...@commandprompt.com>
Date: Tue, 9 Dec 2014 16:35:14 +0300
Subject: [PATCH] WIP: track TRUNCATEs in pgstat transaction stats.

The n_live_tup and n_dead_tup counters need to be set to 0 after a
TRUNCATE on the relation.  We can't issue a special message to the stats
collector because the xact might be later aborted, so we track the fact
that the relation was truncated during the xact (and reset this xact's
insert/update/delete counters).  When xact is committed, we use the
`truncated' flag to reset the n_live_tup and n_dead_tup counters.
---
 src/backend/commands/tablecmds.c             |  3 +
 src/backend/postmaster/pgstat.c              | 98 ++++++++++++++++++++++++++--
 src/include/pgstat.h                         | 14 ++--
 src/test/regress/expected/prepared_xacts.out | 50 ++++++++++++++
 src/test/regress/expected/stats.out          | 63 ++++++++++++++++++
 src/test/regress/sql/prepared_xacts.sql      | 27 ++++++++
 src/test/regress/sql/stats.sql               | 68 +++++++++++++++++++
 7 files changed, 315 insertions(+), 8 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index 1e737a0..4f0e3d8
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 71,76 ****
--- 71,77 ----
  #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"
*************** ExecuteTruncate(TruncateStmt *stmt)
*** 1224,1229 ****
--- 1225,1232 ----
  			 */
  			reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST);
  		}
+ 
+ 		pgstat_count_heap_truncate(rel);
  	}
  
  	/*
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
new file mode 100644
index f71fdeb..9d19cf9
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
*************** typedef struct TwoPhasePgStatRecord
*** 199,206 ****
--- 199,210 ----
  	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;
  
  /*
*************** pgstat_count_heap_delete(Relation rel)
*** 1863,1868 ****
--- 1867,1919 ----
  	}
  }
  
+ static void
+ record_xact_truncate_stats(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;
+ 	}
+ }
+ 
+ static void
+ restore_xact_truncate_stats(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_heap_truncate - update tuple counters due to truncate
+  */
+ void
+ pgstat_count_heap_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);
+ 
+ 		record_xact_truncate_stats(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
   *
*************** AtEOXact_PgStat(bool isCommit)
*** 1921,1932 ****
--- 1972,1993 ----
  			Assert(trans->upper == NULL);
  			tabstat = trans->parent;
  			Assert(tabstat->trans == trans);
+ 			/* restore pre-truncate stats (if any) in case of aborted xact */
+ 			if (!isCommit)
+ 				restore_xact_truncate_stats(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;
*************** AtEOSubXact_PgStat(bool isCommit, int ne
*** 1991,1999 ****
  			{
  				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;
  					tabstat->trans = trans->upper;
  					pfree(trans);
  				}
--- 2052,2072 ----
  			{
  				if (trans->upper && trans->upper->nest_level == nestDepth - 1)
  				{
! 					if (trans->truncated)
! 					{
! 						record_xact_truncate_stats(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);
  				}
*************** AtEOSubXact_PgStat(bool isCommit, int ne
*** 2021,2027 ****
  				 * On abort, update top-level tabstat counts, then forget the
  				 * subtransaction
  				 */
! 
  				/* 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;
--- 2094,2100 ----
  				 * On abort, update top-level tabstat counts, then forget the
  				 * subtransaction
  				 */
! 				restore_xact_truncate_stats(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;
*************** AtPrepare_PgStat(void)
*** 2070,2077 ****
--- 2143,2154 ----
  			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));
*************** pgstat_twophase_postcommit(TransactionId
*** 2137,2142 ****
--- 2214,2221 ----
  	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 +=
*************** pgstat_twophase_postabort(TransactionId
*** 2163,2168 ****
--- 2242,2253 ----
  	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;
*************** pgstat_recv_tabstat(PgStat_MsgTabstat *m
*** 4685,4690 ****
--- 4770,4780 ----
  			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
new file mode 100644
index 0892533..05236be
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
*************** typedef struct PgStat_TableCounts
*** 103,108 ****
--- 103,109 ----
  	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;
*************** typedef struct PgStat_TableStatus
*** 161,170 ****
   */
  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 */
! 	int			nest_level;		/* subtransaction nest level */
  	/* links to other structs for same relation: */
  	struct PgStat_TableXactStatus *upper;		/* next higher subxact if any */
  	PgStat_TableStatus *parent; /* per-table status */
--- 162,175 ----
   */
  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 */
  	PgStat_TableStatus *parent; /* per-table status */
*************** extern void pgstat_initstats(Relation re
*** 916,921 ****
--- 921,927 ----
  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_heap_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
new file mode 100644
index c0b0864..9dbf874
*** a/src/test/regress/expected/prepared_xacts.out
--- b/src/test/regress/expected/prepared_xacts.out
*************** SELECT gid FROM pg_prepared_xacts;
*** 247,254 ****
--- 247,304 ----
  -----
  (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/stats.out b/src/test/regress/expected/stats.out
new file mode 100644
index ec0ff65..7dac69b
*** a/src/test/regress/expected/stats.out
--- b/src/test/regress/expected/stats.out
*************** begin
*** 56,66 ****
--- 56,120 ----
  
    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 
*************** SELECT wait_for_stats();
*** 91,96 ****
--- 145,158 ----
  (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,
*************** SELECT st.heap_blks_read + st.heap_blks_
*** 111,114 ****
--- 173,177 ----
   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
new file mode 100644
index 7902152..56d5857
*** a/src/test/regress/sql/prepared_xacts.sql
--- b/src/test/regress/sql/prepared_xacts.sql
*************** SELECT * FROM pxtest3;
*** 152,158 ****
--- 152,185 ----
  -- 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
new file mode 100644
index 646b9ac..2df17c7
*** a/src/test/regress/sql/stats.sql
--- b/src/test/regress/sql/stats.sql
*************** begin
*** 51,62 ****
--- 51,124 ----
  
    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;
  -- do an indexscan
*************** SELECT pg_sleep(1.0);
*** 70,84 ****
--- 132,152 ----
  SELECT wait_for_stats();
  
  -- 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';
+ 
  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
-- 
2.1.0

-- 
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