#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.
1.7.1