1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Add contrib/pg_freespacemap to display free space map information.

Mark Kirkwood
This commit is contained in:
Bruce Momjian
2006-02-12 03:55:53 +00:00
parent 6c0d4aabe2
commit d5dd3d451e
8 changed files with 481 additions and 98 deletions

View File

@ -0,0 +1,17 @@
# $PostgreSQL: pgsql/contrib/pg_freespacemap/Makefile,v 1.1 2006/02/12 03:55:53 momjian Exp $
MODULE_big = pg_freespacemap
OBJS = pg_freespacemap.o
DATA_built = pg_freespacemap.sql
DOCS = README.pg_freespacemap
ifdef USE_PGXS
PGXS := $(shell pg_config --pgxs)
include $(PGXS)
else
subdir = contrib/pg_freespacemap
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

View File

@ -0,0 +1,98 @@
Pg_freespacemap - Real time queries on the free space map (FSM).
---------------
This module consists of a C function 'pg_freespacemap()' that returns
a set of records, and a view 'pg_freespacemap' to wrapper the function.
The module provides the ability to examine the contents of the free space
map, without having to restart or rebuild the server with additional
debugging code.
By default public access is REVOKED from both of these, just in case there
are security issues lurking.
Installation
------------
Build and install the main Postgresql source, then this contrib module:
$ cd contrib/pg_freespacemap
$ gmake
$ gmake install
To register the functions:
$ psql -d <database> -f pg_freespacemap.sql
Notes
-----
The definition of the columns exposed in the view is:
Column | references | Description
----------------+----------------------+------------------------------------
blockid | | Id, 1.. max_fsm_pages
relfilenode | pg_class.relfilenode | Refilenode of the relation.
reltablespace | pg_tablespace.oid | Tablespace oid of the relation.
reldatabase | pg_database.oid | Database for the relation.
relblocknumber | | Offset of the page in the relation.
blockfreebytes | | Free bytes in the block/page.
There is one row for each page in the free space map.
Because the map is shared by all the databases, there are pages from
relations not belonging to the current database.
When the pg_freespacemap view is accessed, internal free space map locks are
taken, and a copy of the map data is made for the view to display.
This ensures that the view produces a consistent set of results, while not
blocking normal activity longer than necessary. Nonetheless there
could be some impact on database performance if this view is read often.
Sample output
-------------
regression=# \d pg_freespacemap
View "public.pg_freespacemap"
Column | Type | Modifiers
---------------+---------+-----------
blockid | integer |
relfilenode | oid |
reltablespace | oid |
reldatabase | oid |
relblocknumber | bigint |
blockfreebytes | integer |
View definition:
SELECT p.blockid, p.relfilenode, p.reltablespace, p.reldatabase, p.relblocknumber, p.blockfreebytes
FROM pg_freespacemap() p(blockid integer, relfilenode oid, reltablespace oid, reldatabase oid, relblocknumber bigint, blockfreebytes integer);
regression=# SELECT c.relname, m.relblocknumber, m.blockfreebytes
FROM pg_freespacemap m INNER JOIN pg_class c
ON c.relfilenode = m.relfilenode LIMIT 10;
relname | relblocknumber | blockfreebytes
------------------------+----------------+----------------
sql_features | 5 | 2696
sql_implementation_info | 0 | 7104
sql_languages | 0 | 8016
sql_packages | 0 | 7376
sql_sizing | 0 | 6032
pg_authid | 0 | 7424
pg_toast_2618 | 13 | 4588
pg_toast_2618 | 12 | 1680
pg_toast_2618 | 10 | 1436
pg_toast_2618 | 7 | 1136
(10 rows)
regression=#
Author
------
* Mark Kirkwood <markir@paradise.net.nz>

View File

@ -0,0 +1,231 @@
/*-------------------------------------------------------------------------
*
* pg_freespacemap.c
* display some contents of the free space map.
*
* $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.1 2006/02/12 03:55:53 momjian Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "funcapi.h"
#include "catalog/pg_type.h"
#include "storage/freespace.h"
#include "utils/relcache.h"
#define NUM_FREESPACE_PAGES_ELEM 6
#if defined(WIN32) || defined(__CYGWIN__)
extern DLLIMPORT volatile uint32 InterruptHoldoffCount;
#endif
Datum pg_freespacemap(PG_FUNCTION_ARGS);
/*
* Record structure holding the to be exposed free space data.
*/
typedef struct
{
uint32 blockid;
uint32 relfilenode;
uint32 reltablespace;
uint32 reldatabase;
uint32 relblocknumber;
uint32 blockfreebytes;
} FreeSpacePagesRec;
/*
* Function context for data persisting over repeated calls.
*/
typedef struct
{
AttInMetadata *attinmeta;
FreeSpacePagesRec *record;
char *values[NUM_FREESPACE_PAGES_ELEM];
} FreeSpacePagesContext;
/*
* Function returning data from the Free Space Map (FSM).
*/
PG_FUNCTION_INFO_V1(pg_freespacemap);
Datum
pg_freespacemap(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
Datum result;
MemoryContext oldcontext;
FreeSpacePagesContext *fctx; /* User function context. */
TupleDesc tupledesc;
HeapTuple tuple;
FSMHeader *FreeSpaceMap; /* FSM main structure. */
FSMRelation *fsmrel; /* Individual relation. */
if (SRF_IS_FIRSTCALL())
{
uint32 i;
uint32 numPages; /* Max possible no. of pages in map. */
int nPages; /* Mapped pages for a relation. */
/*
* Get the free space map data structure.
*/
FreeSpaceMap = GetFreeSpaceMap();
numPages = MaxFSMPages;
funcctx = SRF_FIRSTCALL_INIT();
/* Switch context when allocating stuff to be used in later calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* Construct a tuple to return. */
tupledesc = CreateTemplateTupleDesc(NUM_FREESPACE_PAGES_ELEM, false);
TupleDescInitEntry(tupledesc, (AttrNumber) 1, "blockid",
INT4OID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
OIDOID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
OIDOID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
OIDOID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relblocknumber",
INT8OID, -1, 0);
TupleDescInitEntry(tupledesc, (AttrNumber) 6, "blockfreebytes",
INT4OID, -1, 0);
/* Generate attribute metadata needed later to produce tuples */
funcctx->attinmeta = TupleDescGetAttInMetadata(tupledesc);
/*
* Create a function context for cross-call persistence and initialize
* the counters.
*/
fctx = (FreeSpacePagesContext *) palloc(sizeof(FreeSpacePagesContext));
funcctx->user_fctx = fctx;
/* Set an upper bound on the calls */
funcctx->max_calls = numPages;
/* Allocate numPages worth of FreeSpacePagesRec records, this is also
* an upper bound.
*/
fctx->record = (FreeSpacePagesRec *) palloc(sizeof(FreeSpacePagesRec) * numPages);
/* allocate the strings for tuple formation */
fctx->values[0] = (char *) palloc(3 * sizeof(uint32) + 1);
fctx->values[1] = (char *) palloc(3 * sizeof(uint32) + 1);
fctx->values[2] = (char *) palloc(3 * sizeof(uint32) + 1);
fctx->values[3] = (char *) palloc(3 * sizeof(uint32) + 1);
fctx->values[4] = (char *) palloc(3 * sizeof(uint32) + 1);
fctx->values[5] = (char *) palloc(3 * sizeof(uint32) + 1);
/* Return to original context when allocating transient memory */
MemoryContextSwitchTo(oldcontext);
/*
* Lock free space map and scan though all the relations,
* for each relation, gets all its mapped pages.
*/
LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE);
i = 0;
for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage)
{
if (fsmrel->isIndex)
{ /* Index relation. */
IndexFSMPageData *page;
page = (IndexFSMPageData *)
(FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
for (nPages = 0; nPages < fsmrel->storedPages; nPages++)
{
fctx->record[i].blockid = i;
fctx->record[i].relfilenode = fsmrel->key.relNode;
fctx->record[i].reltablespace = fsmrel->key.spcNode;
fctx->record[i].reldatabase = fsmrel->key.dbNode;
fctx->record[i].relblocknumber = IndexFSMPageGetPageNum(page);
fctx->record[i].blockfreebytes = 0; /* index.*/
page++;
i++;
}
}
else
{ /* Heap relation. */
FSMPageData *page;
page = (FSMPageData *)
(FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
for (nPages = 0; nPages < fsmrel->storedPages; nPages++)
{
fctx->record[i].blockid = i;
fctx->record[i].relfilenode = fsmrel->key.relNode;
fctx->record[i].reltablespace = fsmrel->key.spcNode;
fctx->record[i].reldatabase = fsmrel->key.dbNode;
fctx->record[i].relblocknumber = FSMPageGetPageNum(page);
fctx->record[i].blockfreebytes = FSMPageGetSpace(page);
page++;
i++;
}
}
}
/* Set the real no. of calls as we know it now! */
funcctx->max_calls = i;
/* Release free space map. */
LWLockRelease(FreeSpaceLock);
}
funcctx = SRF_PERCALL_SETUP();
/* Get the saved state */
fctx = funcctx->user_fctx;
if (funcctx->call_cntr < funcctx->max_calls)
{
uint32 i = funcctx->call_cntr;
sprintf(fctx->values[0], "%u", fctx->record[i].blockid);
sprintf(fctx->values[1], "%u", fctx->record[i].relfilenode);
sprintf(fctx->values[2], "%u", fctx->record[i].reltablespace);
sprintf(fctx->values[3], "%u", fctx->record[i].reldatabase);
sprintf(fctx->values[4], "%u", fctx->record[i].relblocknumber);
sprintf(fctx->values[5], "%u", fctx->record[i].blockfreebytes);
/* Build and return the tuple. */
tuple = BuildTupleFromCStrings(funcctx->attinmeta, fctx->values);
result = HeapTupleGetDatum(tuple);
SRF_RETURN_NEXT(funcctx, result);
}
else
SRF_RETURN_DONE(funcctx);
}

View File

@ -0,0 +1,19 @@
-- Adjust this setting to control where the objects get created.
BEGIN;
SET search_path = public;
-- Register the function.
CREATE OR REPLACE FUNCTION pg_freespacemap()
RETURNS SETOF RECORD
AS 'MODULE_PATHNAME', 'pg_freespacemap'
LANGUAGE 'C';
-- Create a view for convenient access.
CREATE VIEW pg_freespacemap AS
SELECT P.* FROM pg_freespacemap() AS P
(blockid int4, relfilenode oid, reltablespace oid, reldatabase oid, relblocknumber int8, blockfreebytes int4);
-- Don't want these to be available at public.
REVOKE ALL ON FUNCTION pg_freespacemap() FROM PUBLIC;
REVOKE ALL ON pg_freespacemap FROM PUBLIC;
COMMIT;