#include "postgres.h"
#include "access/gist_private.h"
#include "access/hash.h"
#include "access/nbtree.h"
#include "access/relscan.h"
#include "catalog/namespace.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/tqual.h"
Go to the source code of this file.
#define NCHARS 32 |
#define NCOLUMNS 9 |
typedef void(* pgstat_page)(pgstattuple_type *, Relation, BlockNumber, BufferAccessStrategy) |
Definition at line 64 of file pgstattuple.c.
typedef struct pgstattuple_type pgstattuple_type |
static Datum build_pgstattuple_type | ( | pgstattuple_type * | stat, | |
FunctionCallInfo | fcinfo | |||
) | [static] |
Definition at line 89 of file pgstattuple.c.
References BuildTupleFromCStrings(), pgstattuple_type::dead_tuple_count, pgstattuple_type::dead_tuple_len, elog, ERROR, pgstattuple_type::free_space, get_call_result_type(), HeapTupleGetDatum, i, NCHARS, NULL, snprintf(), pgstattuple_type::table_len, pgstattuple_type::tuple_count, pgstattuple_type::tuple_len, TupleDescGetAttInMetadata(), TYPEFUNC_COMPOSITE, and values.
Referenced by pgstat_heap(), and pgstat_index().
{ #define NCOLUMNS 9 #define NCHARS 32 HeapTuple tuple; char *values[NCOLUMNS]; char values_buf[NCOLUMNS][NCHARS]; int i; double tuple_percent; double dead_tuple_percent; double free_percent; /* free/reusable space in % */ TupleDesc tupdesc; AttInMetadata *attinmeta; /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta = TupleDescGetAttInMetadata(tupdesc); if (stat->table_len == 0) { tuple_percent = 0.0; dead_tuple_percent = 0.0; free_percent = 0.0; } else { tuple_percent = 100.0 * stat->tuple_len / stat->table_len; dead_tuple_percent = 100.0 * stat->dead_tuple_len / stat->table_len; free_percent = 100.0 * stat->free_space / stat->table_len; } /* * Prepare a values array for constructing the tuple. This should be an * array of C strings which will be processed later by the appropriate * "in" functions. */ for (i = 0; i < NCOLUMNS; i++) values[i] = values_buf[i]; i = 0; snprintf(values[i++], NCHARS, INT64_FORMAT, stat->table_len); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->tuple_len); snprintf(values[i++], NCHARS, "%.2f", tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_count); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->dead_tuple_len); snprintf(values[i++], NCHARS, "%.2f", dead_tuple_percent); snprintf(values[i++], NCHARS, INT64_FORMAT, stat->free_space); snprintf(values[i++], NCHARS, "%.2f", free_percent); /* build a tuple */ tuple = BuildTupleFromCStrings(attinmeta, values); /* make the tuple into a datum */ return HeapTupleGetDatum(tuple); }
PG_FUNCTION_INFO_V1 | ( | pgstattuple | ) |
PG_FUNCTION_INFO_V1 | ( | pgstattuplebyid | ) |
static void pgstat_btree_page | ( | pgstattuple_type * | stat, | |
Relation | rel, | |||
BlockNumber | blkno, | |||
BufferAccessStrategy | bstrategy | |||
) | [static] |
Definition at line 355 of file pgstattuple.c.
References _bt_relbuf(), BT_READ, BTP_DELETED, BTP_HALF_DEAD, BTPageOpaqueData::btpo_flags, buf, BufferGetPage, pgstattuple_type::free_space, LockBuffer(), MAIN_FORKNUM, P_FIRSTDATAKEY, P_ISLEAF, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageIsNew, pgstat_index_page(), RBM_NORMAL, and ReadBufferExtended().
Referenced by pgstat_relation().
{ Buffer buf; Page page; buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); LockBuffer(buf, BT_READ); page = BufferGetPage(buf); /* Page is valid, see what to do with it */ if (PageIsNew(page)) { /* fully empty page */ stat->free_space += BLCKSZ; } else { BTPageOpaque opaque; opaque = (BTPageOpaque) PageGetSpecialPointer(page); if (opaque->btpo_flags & (BTP_DELETED | BTP_HALF_DEAD)) { /* recyclable page */ stat->free_space += BLCKSZ; } else if (P_ISLEAF(opaque)) { pgstat_index_page(stat, page, P_FIRSTDATAKEY(opaque), PageGetMaxOffsetNumber(page)); } else { /* root or node */ } } _bt_relbuf(rel, buf); }
static void pgstat_gist_page | ( | pgstattuple_type * | stat, | |
Relation | rel, | |||
BlockNumber | blkno, | |||
BufferAccessStrategy | bstrategy | |||
) | [static] |
Definition at line 443 of file pgstattuple.c.
References buf, BufferGetPage, FirstOffsetNumber, GIST_SHARE, gistcheckpage(), GistPageIsLeaf, LockBuffer(), MAIN_FORKNUM, PageGetMaxOffsetNumber, pgstat_index_page(), RBM_NORMAL, ReadBufferExtended(), and UnlockReleaseBuffer().
Referenced by pgstat_relation().
{ Buffer buf; Page page; buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy); LockBuffer(buf, GIST_SHARE); gistcheckpage(rel, buf); page = BufferGetPage(buf); if (GistPageIsLeaf(page)) { pgstat_index_page(stat, page, FirstOffsetNumber, PageGetMaxOffsetNumber(page)); } else { /* root or node */ } UnlockReleaseBuffer(buf); }
static void pgstat_hash_page | ( | pgstattuple_type * | stat, | |
Relation | rel, | |||
BlockNumber | blkno, | |||
BufferAccessStrategy | bstrategy | |||
) | [static] |
Definition at line 399 of file pgstattuple.c.
References _hash_droplock(), _hash_getbuf_with_strategy(), _hash_getlock(), _hash_relbuf(), buf, BufferGetPage, FirstOffsetNumber, pgstattuple_type::free_space, HASH_READ, HASH_SHARE, HashPageOpaqueData::hasho_flag, LH_BITMAP_PAGE, LH_BUCKET_PAGE, LH_META_PAGE, LH_OVERFLOW_PAGE, LH_UNUSED_PAGE, MAXALIGN, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageGetSpecialSize, and pgstat_index_page().
Referenced by pgstat_relation().
{ Buffer buf; Page page; _hash_getlock(rel, blkno, HASH_SHARE); buf = _hash_getbuf_with_strategy(rel, blkno, HASH_READ, 0, bstrategy); page = BufferGetPage(buf); if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HashPageOpaqueData))) { HashPageOpaque opaque; opaque = (HashPageOpaque) PageGetSpecialPointer(page); switch (opaque->hasho_flag) { case LH_UNUSED_PAGE: stat->free_space += BLCKSZ; break; case LH_BUCKET_PAGE: case LH_OVERFLOW_PAGE: pgstat_index_page(stat, page, FirstOffsetNumber, PageGetMaxOffsetNumber(page)); break; case LH_BITMAP_PAGE: case LH_META_PAGE: default: break; } } else { /* maybe corrupted */ } _hash_relbuf(rel, buf); _hash_droplock(rel, blkno, HASH_SHARE); }
static Datum pgstat_heap | ( | Relation | rel, | |
FunctionCallInfo | fcinfo | |||
) | [static] |
Definition at line 271 of file pgstattuple.c.
References AccessShareLock, BAS_BULKREAD, BlockIdGetBlockNumber, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage, build_pgstattuple_type(), CHECK_FOR_INTERRUPTS, pgstattuple_type::dead_tuple_count, pgstattuple_type::dead_tuple_len, ForwardScanDirection, pgstattuple_type::free_space, GetAccessStrategy(), heap_beginscan_strat(), heap_endscan(), heap_getnext(), HeapTupleSatisfiesVisibility, ItemPointerData::ip_blkid, LockBuffer(), MAIN_FORKNUM, NULL, PageGetHeapFreeSpace(), RBM_NORMAL, ReadBufferExtended(), relation_close(), HeapScanDescData::rs_cbuf, HeapScanDescData::rs_nblocks, HeapScanDescData::rs_strategy, SnapshotAny, SnapshotNow, HeapTupleData::t_len, HeapTupleData::t_self, pgstattuple_type::table_len, pgstattuple_type::tuple_count, pgstattuple_type::tuple_len, and UnlockReleaseBuffer().
Referenced by pgstat_relation().
{ HeapScanDesc scan; HeapTuple tuple; BlockNumber nblocks; BlockNumber block = 0; /* next block to count free space in */ BlockNumber tupblock; Buffer buffer; pgstattuple_type stat = {0}; BufferAccessStrategy bstrategy; /* Disable syncscan because we assume we scan from block zero upwards */ scan = heap_beginscan_strat(rel, SnapshotAny, 0, NULL, true, false); nblocks = scan->rs_nblocks; /* # blocks to be scanned */ /* prepare access strategy for this table */ bstrategy = GetAccessStrategy(BAS_BULKREAD); scan->rs_strategy = bstrategy; /* scan the relation */ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { CHECK_FOR_INTERRUPTS(); /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesVisibility(tuple, SnapshotNow, scan->rs_cbuf)) { stat.tuple_len += tuple->t_len; stat.tuple_count++; } else { stat.dead_tuple_len += tuple->t_len; stat.dead_tuple_count++; } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); /* * 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) { CHECK_FOR_INTERRUPTS(); buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block, RBM_NORMAL, bstrategy); LockBuffer(buffer, BUFFER_LOCK_SHARE); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer)); UnlockReleaseBuffer(buffer); block++; } } heap_endscan(scan); while (block < nblocks) { CHECK_FOR_INTERRUPTS(); buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block, RBM_NORMAL, bstrategy); LockBuffer(buffer, BUFFER_LOCK_SHARE); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer)); UnlockReleaseBuffer(buffer); block++; } relation_close(rel, AccessShareLock); stat.table_len = (uint64) nblocks *BLCKSZ; return build_pgstattuple_type(&stat, fcinfo); }
static Datum pgstat_index | ( | Relation | rel, | |
BlockNumber | start, | |||
pgstat_page | pagefn, | |||
FunctionCallInfo | fcinfo | |||
) | [static] |
Definition at line 471 of file pgstattuple.c.
References AccessShareLock, BAS_BULKREAD, build_pgstattuple_type(), CHECK_FOR_INTERRUPTS, ExclusiveLock, GetAccessStrategy(), LockRelationForExtension(), relation_close(), RelationGetNumberOfBlocks, pgstattuple_type::table_len, and UnlockRelationForExtension().
Referenced by pgstat_relation().
{ BlockNumber nblocks; BlockNumber blkno; BufferAccessStrategy bstrategy; pgstattuple_type stat = {0}; /* prepare access strategy for this index */ bstrategy = GetAccessStrategy(BAS_BULKREAD); blkno = start; for (;;) { /* Get the current relation length */ LockRelationForExtension(rel, ExclusiveLock); nblocks = RelationGetNumberOfBlocks(rel); UnlockRelationForExtension(rel, ExclusiveLock); /* Quit if we've scanned the whole relation */ if (blkno >= nblocks) { stat.table_len = (uint64) nblocks *BLCKSZ; break; } for (; blkno < nblocks; blkno++) { CHECK_FOR_INTERRUPTS(); pagefn(&stat, rel, blkno, bstrategy); } } relation_close(rel, AccessShareLock); return build_pgstattuple_type(&stat, fcinfo); }
static void pgstat_index_page | ( | pgstattuple_type * | stat, | |
Page | page, | |||
OffsetNumber | minoff, | |||
OffsetNumber | maxoff | |||
) | [static] |
Definition at line 515 of file pgstattuple.c.
References pgstattuple_type::dead_tuple_count, pgstattuple_type::dead_tuple_len, pgstattuple_type::free_space, i, ItemIdGetLength, ItemIdIsDead, OffsetNumberNext, PageGetFreeSpace(), PageGetItemId, pgstattuple_type::tuple_count, and pgstattuple_type::tuple_len.
Referenced by pgstat_btree_page(), pgstat_gist_page(), and pgstat_hash_page().
{ OffsetNumber i; stat->free_space += PageGetFreeSpace(page); for (i = minoff; i <= maxoff; i = OffsetNumberNext(i)) { ItemId itemid = PageGetItemId(page, i); if (ItemIdIsDead(itemid)) { stat->dead_tuple_count++; stat->dead_tuple_len += ItemIdGetLength(itemid); } else { stat->tuple_count++; stat->tuple_len += ItemIdGetLength(itemid); } } }
static Datum pgstat_relation | ( | Relation | rel, | |
FunctionCallInfo | fcinfo | |||
) | [static] |
Definition at line 202 of file pgstattuple.c.
References BTREE_AM_OID, BTREE_METAPAGE, ereport, errcode(), errmsg(), ERROR, GIN_AM_OID, GIST_AM_OID, GIST_ROOT_BLKNO, HASH_AM_OID, HASH_METAPAGE, pgstat_btree_page(), pgstat_gist_page(), pgstat_hash_page(), pgstat_heap(), pgstat_index(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetRelationName, RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE, RELKIND_INDEX, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_TOASTVALUE, RELKIND_VIEW, and SPGIST_AM_OID.
Referenced by pgstattuple(), and pgstattuplebyid().
{ const char *err; /* * Reject attempts to read non-local temporary relations; we would be * likely to get wrong data since we have no visibility into the owning * session's local buffers. */ if (RELATION_IS_OTHER_TEMP(rel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot access temporary tables of other sessions"))); switch (rel->rd_rel->relkind) { case RELKIND_RELATION: case RELKIND_MATVIEW: case RELKIND_TOASTVALUE: case RELKIND_SEQUENCE: return pgstat_heap(rel, fcinfo); case RELKIND_INDEX: switch (rel->rd_rel->relam) { case BTREE_AM_OID: return pgstat_index(rel, BTREE_METAPAGE + 1, pgstat_btree_page, fcinfo); case HASH_AM_OID: return pgstat_index(rel, HASH_METAPAGE + 1, pgstat_hash_page, fcinfo); case GIST_AM_OID: return pgstat_index(rel, GIST_ROOT_BLKNO + 1, pgstat_gist_page, fcinfo); case GIN_AM_OID: err = "gin index"; break; case SPGIST_AM_OID: err = "spgist index"; break; default: err = "unknown index"; break; } break; case RELKIND_VIEW: err = "view"; break; case RELKIND_COMPOSITE_TYPE: err = "composite type"; break; case RELKIND_FOREIGN_TABLE: err = "foreign table"; break; default: err = "unknown"; break; } ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("\"%s\" (%s) is not supported", RelationGetRelationName(rel), err))); return 0; /* should not happen */ }
Datum pgstattuple | ( | PG_FUNCTION_ARGS | ) |
Definition at line 163 of file pgstattuple.c.
References AccessShareLock, ereport, errcode(), errmsg(), ERROR, makeRangeVarFromNameList(), PG_GETARG_TEXT_P, PG_RETURN_DATUM, pgstat_relation(), relation_openrv(), superuser(), and textToQualifiedNameList().
{ text *relname = PG_GETARG_TEXT_P(0); RangeVar *relrv; Relation rel; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); /* open relation */ relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); }
Datum pgstattuplebyid | ( | PG_FUNCTION_ARGS | ) |
Definition at line 182 of file pgstattuple.c.
References AccessShareLock, ereport, errcode(), errmsg(), ERROR, PG_GETARG_OID, PG_RETURN_DATUM, pgstat_relation(), relation_open(), and superuser().
{ Oid relid = PG_GETARG_OID(0); Relation rel; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use pgstattuple functions")))); /* open relation */ rel = relation_open(relid, AccessShareLock); PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); }
Definition at line 40 of file pgstattuple.c.