2 * contrib/pgstattuple/pgstattuple.c
4 * Copyright (c) 2001,2002 Tatsuo Ishii
6 * Permission to use, copy, modify, and distribute this software and
7 * its documentation for any purpose, without fee, and without a
8 * written agreement is hereby granted, provided that the above
9 * copyright notice and this paragraph and the following two
10 * paragraphs appear in all copies.
12 * IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
13 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
16 * OF THE POSSIBILITY OF SUCH DAMAGE.
18 * THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
21 * IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
22 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
34#include "catalog/pg_am_d.h"
42 .
name =
"pgstattuple",
52 * struct pgstattuple_type
54 * tuple_percent, dead_tuple_percent and free_percent are computable,
55 * so not defined here.
89 * build_pgstattuple_type -- build a pgstattuple_type tuple
101 double tuple_percent;
102 double dead_tuple_percent;
103 double free_percent;
/* free/reusable space in % */
107 /* Build a tuple descriptor for our result type */
109 elog(
ERROR,
"return type must be a row type");
112 * Generate attribute metadata needed later to produce tuples from raw C
117 if (
stat->table_len == 0)
120 dead_tuple_percent = 0.0;
125 tuple_percent = 100.0 *
stat->tuple_len /
stat->table_len;
126 dead_tuple_percent = 100.0 *
stat->dead_tuple_len /
stat->table_len;
127 free_percent = 100.0 *
stat->free_space /
stat->table_len;
131 * Prepare a values array for constructing the tuple. This should be an
132 * array of C strings which will be processed later by the appropriate
151 /* make the tuple into a datum */
157 * returns live/dead tuples info
159 * C FUNCTION definition
160 * pgstattuple(text) returns pgstattuple_type
162 * The superuser() check here must be kept as the library might be upgraded
163 * without the extension being upgraded, meaning that in pre-1.5 installations
164 * these functions could be called by any user.
177 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
178 errmsg(
"must be superuser to use pgstattuple functions")));
188 * As of pgstattuple version 1.5, we no longer need to check if the user
189 * is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
190 * Users can then grant access to it based on their policies.
192 * Otherwise identical to pgstattuple (above).
208/* Must keep superuser() check, see above. */
217 (
errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
218 errmsg(
"must be superuser to use pgstattuple functions")));
226/* Remove superuser() check for 1.5 version, see above */
248 * Reject attempts to read non-local temporary relations; we would be
249 * likely to get wrong data since we have no visibility into the owning
250 * session's local buffers.
254 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
255 errmsg(
"cannot access temporary tables of other sessions")));
257 if (RELKIND_HAS_TABLE_AM(rel->
rd_rel->relkind) ||
258 rel->
rd_rel->relkind == RELKIND_SEQUENCE)
262 else if (rel->
rd_rel->relkind == RELKIND_INDEX)
264 /* see pgstatindex_impl */
267 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
268 errmsg(
"index \"%s\" is not valid",
271 switch (rel->
rd_rel->relam)
286 err =
"spgist index";
292 err =
"unknown index";
296 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
297 errmsg(
"index \"%s\" (%s) is not supported",
303 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
304 errmsg(
"cannot get tuple-level statistics for relation \"%s\"",
309 return 0;
/* should not happen */
313 * pgstat_heap -- returns live/dead tuples info in a heap
322 BlockNumber block = 0;
/* next block to count free space in */
329 * Sequences always use heap AM, but they don't show that in the catalogs.
331 if (rel->
rd_rel->relkind != RELKIND_SEQUENCE &&
332 rel->
rd_rel->relam != HEAP_TABLE_AM_OID)
334 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
335 errmsg(
"only heap AM is supported")));
337 /* Disable syncscan because we assume we scan from block zero upwards */
343 nblocks = hscan->
rs_nblocks;
/* # blocks to be scanned */
345 /* scan the relation */
350 /* must hold a buffer lock to call HeapTupleSatisfiesVisibility */
361 stat.dead_tuple_count++;
367 * To avoid physically reading the table twice, try to do the
368 * free-space scan in parallel with the heap scan. However,
369 * heap_getnext may find no tuples on a given page, so we cannot
370 * simply examine the pages returned by the heap scan.
374 while (block <= tupblock)
387 while (block < nblocks)
408 * pgstat_btree_page -- check tuples in a btree page
421 /* Page is valid, see what to do with it */
424 /* fully empty page */
425 stat->free_space += BLCKSZ;
434 /* deleted or half-dead page */
435 stat->free_space += BLCKSZ;
452 * pgstat_hash_page -- check tuples in a hash page
467 /* fully empty page */
468 stat->free_space += BLCKSZ;
478 stat->free_space += BLCKSZ;
493 /* maybe corrupted */
500 * pgstat_gist_page -- check tuples in a gist page
514 /* fully empty page */
515 stat->free_space += BLCKSZ;
534 * pgstat_index -- returns live/dead tuples info in a generic index
545 /* prepare access strategy for this index */
551 /* Get the current relation length */
556 /* Quit if we've scanned the whole relation */
557 if (blkno >= nblocks)
564 for (; blkno < nblocks; blkno++)
568 pagefn(&
stat, rel, blkno, bstrategy);
578 * pgstat_index_page -- for generic index page
594 stat->dead_tuple_count++;
static Datum values[MAXATTR]
struct BufferAccessStrategyData * BufferAccessStrategy
void UnlockReleaseBuffer(Buffer buffer)
void LockBuffer(Buffer buffer, int mode)
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
#define BUFFER_LOCK_UNLOCK
#define BUFFER_LOCK_SHARE
#define RelationGetNumberOfBlocks(reln)
static Page BufferGetPage(Buffer buffer)
Size PageGetExactFreeSpace(const PageData *page)
static uint16 PageGetSpecialSize(const PageData *page)
static bool PageIsNew(const PageData *page)
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void err(int eval, const char *fmt,...)
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
#define PG_GETARG_TEXT_PP(n)
#define PG_RETURN_DATUM(x)
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
struct RelationData * Relation
#define GistPageIsLeaf(page)
#define HashPageGetOpaque(page)
void _hash_relbuf(Relation rel, Buffer buf)
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
struct HeapScanDescData * HeapScanDesc
bool HeapTupleSatisfiesVisibility(HeapTuple htup, Snapshot snapshot, Buffer buffer)
#define ItemIdGetLength(itemId)
#define ItemIdIsDead(itemId)
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
#define CHECK_FOR_INTERRUPTS()
RangeVar * makeRangeVarFromNameList(const List *names)
void _bt_relbuf(Relation rel, Buffer buf)
#define BTPageGetOpaque(page)
#define P_FIRSTDATAKEY(opaque)
#define OffsetNumberNext(offsetNumber)
#define FirstOffsetNumber
int errdetail_relkind_not_supported(char relkind)
Datum pgstattuplebyid(PG_FUNCTION_ARGS)
static Datum pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
PG_MODULE_MAGIC_EXT(.name="pgstattuple",.version=PG_VERSION)
static Datum pgstat_index(Relation rel, BlockNumber start, pgstat_page pagefn, FunctionCallInfo fcinfo)
static void pgstat_index_page(pgstattuple_type *stat, Page page, OffsetNumber minoff, OffsetNumber maxoff)
Datum pgstattuple_v1_5(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(pgstattuple)
static void pgstat_gist_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, BufferAccessStrategy bstrategy)
struct pgstattuple_type pgstattuple_type
void(* pgstat_page)(pgstattuple_type *, Relation, BlockNumber, BufferAccessStrategy)
static Datum build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo)
static void pgstat_btree_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, BufferAccessStrategy bstrategy)
Datum pgstattuplebyid_v1_5(PG_FUNCTION_ARGS)
static Datum pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
Datum pgstattuple(PG_FUNCTION_ARGS)
static void pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, BufferAccessStrategy bstrategy)
#define RelationGetRelationName(relation)
#define RELATION_IS_OTHER_TEMP(relation)
#define InitDirtySnapshot(snapshotdata)
void relation_close(Relation relation, LOCKMODE lockmode)
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
BufferAccessStrategy rs_strategy
static void table_endscan(TableScanDesc scan)
static TableScanDesc table_beginscan_strat(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, bool allow_strat, bool allow_sync)
List * textToQualifiedNameList(text *textval)