From 897083715b282881e5d85ba55285708c24cc2abb Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 19 Dec 2001 20:28:41 +0000 Subject: [PATCH] Fix pgstattuple to acquire a read lock on the target table. This prevents embarassments such as having the table dropped or truncated partway through the scan. Also, fix free space calculation to include pages that currently contain no tuples. --- contrib/pgstattuple/README.pgstattuple | 4 +- contrib/pgstattuple/pgstattuple.c | 54 +++++++++++++++++--------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/contrib/pgstattuple/README.pgstattuple b/contrib/pgstattuple/README.pgstattuple index c54f97e82f..6a191e9cce 100644 --- a/contrib/pgstattuple/README.pgstattuple +++ b/contrib/pgstattuple/README.pgstattuple @@ -15,7 +15,7 @@ NOTICE: physical length: 0.08MB live tuples: 20 (0.00MB, 1.17%) dead tuples: 32 18.75 (1 row) - Above example shows tellers tables includes 18.75% dead tuples. + Above example shows tellers table includes 18.75% dead tuples. physical length physical size of the table in MB live tuples information on the live tuples @@ -40,7 +40,7 @@ NOTICE: physical length: 0.08MB live tuples: 20 (0.00MB, 1.17%) dead tuples: 32 4. Notes - pgstattuple does not lock the target table at all. So concurrent + pgstattuple acquires only a read lock on the table. So concurrent update may affect the result. pgstattuple judges a tuple is "dead" if HeapTupleSatisfiesNow() diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 45e9229810..b5afab80de 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -1,5 +1,5 @@ /* - * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.2 2001/10/25 05:49:20 momjian Exp $ + * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.3 2001/12/19 20:28:41 tgl Exp $ * * Copyright (c) 2001 Tatsuo Ishii * @@ -23,6 +23,7 @@ */ #include "postgres.h" + #include "fmgr.h" #include "access/heapam.h" #include "access/transam.h" @@ -48,20 +49,21 @@ pgstattuple(PG_FUNCTION_ARGS) HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; - BlockNumber block = InvalidBlockNumber; + BlockNumber block = 0; /* next block to count free space in */ + BlockNumber tupblock; + Buffer buffer; double table_len; uint64 tuple_len = 0; uint64 dead_tuple_len = 0; - uint32 tuple_count = 0; - uint32 dead_tuple_count = 0; + uint64 tuple_count = 0; + uint64 dead_tuple_count = 0; double tuple_percent; double dead_tuple_percent; - Buffer buffer = InvalidBuffer; uint64 free_space = 0; /* free/reusable space in bytes */ double free_percent; /* free/reusable space in % */ - rel = heap_openr(NameStr(*p), NoLock); + rel = heap_openr(NameStr(*p), AccessShareLock); nblocks = RelationGetNumberOfBlocks(rel); scan = heap_beginscan(rel, false, SnapshotAny, 0, NULL); @@ -78,17 +80,33 @@ pgstattuple(PG_FUNCTION_ARGS) dead_tuple_count++; } - if (!BlockNumberIsValid(block) || - block != BlockIdGetBlockNumber(&tuple->t_self.ip_blkid)) + /* + * To avoid physically reading the table twice, try to do the + * free-space scan in parallel with the heap scan. However, + * heap_getnext may find no tuples on a given page, so we cannot + * simply examine the pages returned by the heap scan. + */ + tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); + + while (block <= tupblock) { - block = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); buffer = ReadBuffer(rel, block); free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); ReleaseBuffer(buffer); + block++; } } heap_endscan(scan); - heap_close(rel, NoLock); + + while (block < nblocks) + { + buffer = ReadBuffer(rel, block); + free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); + ReleaseBuffer(buffer); + block++; + } + + heap_close(rel, AccessShareLock); table_len = (double) nblocks *BLCKSZ; @@ -105,20 +123,20 @@ pgstattuple(PG_FUNCTION_ARGS) free_percent = (double) free_space *100.0 / table_len; } - elog(NOTICE, "physical length: %.2fMB live tuples: %u (%.2fMB, %.2f%%) dead tuples: %u (%.2fMB, %.2f%%) free/reusable space: %.2fMB (%.2f%%) overhead: %.2f%%", + elog(NOTICE, "physical length: %.2fMB live tuples: %.0f (%.2fMB, %.2f%%) dead tuples: %.0f (%.2fMB, %.2f%%) free/reusable space: %.2fMB (%.2f%%) overhead: %.2f%%", - table_len / 1024 / 1024, /* phsical length in MB */ + table_len / (1024 * 1024), /* physical length in MB */ - tuple_count, /* number of live tuples */ - (double) tuple_len / 1024 / 1024, /* live tuples in MB */ + (double) tuple_count, /* number of live tuples */ + (double) tuple_len / (1024 * 1024), /* live tuples in MB */ tuple_percent, /* live tuples in % */ - dead_tuple_count, /* number of dead tuples */ - (double) dead_tuple_len / 1024 / 1024, /* dead tuples in MB */ + (double) dead_tuple_count, /* number of dead tuples */ + (double) dead_tuple_len / (1024 * 1024), /* dead tuples in MB */ dead_tuple_percent, /* dead tuples in % */ - (double) free_space / 1024 / 1024, /* free/available space in - * MB */ + (double) free_space / (1024 * 1024), /* free/available space in + * MB */ free_percent, /* free/available space in % */