summaryrefslogtreecommitdiff
path: root/contrib/pg_freespacemap
diff options
context:
space:
mode:
authorBruce Momjian2006-02-12 03:55:53 +0000
committerBruce Momjian2006-02-12 03:55:53 +0000
commitd5dd3d451e7d898ea2492d80539f74f032e3f408 (patch)
tree2145f2c3a99667d93a69e43fb5ae7775ddb983f6 /contrib/pg_freespacemap
parent6c0d4aabe2fd36e2e808c2d453fcabbe6c8464fa (diff)
Add contrib/pg_freespacemap to display free space map information.
Mark Kirkwood
Diffstat (limited to 'contrib/pg_freespacemap')
-rw-r--r--contrib/pg_freespacemap/Makefile17
-rw-r--r--contrib/pg_freespacemap/README.pg_freespacemap98
-rw-r--r--contrib/pg_freespacemap/pg_freespacemap.c231
-rw-r--r--contrib/pg_freespacemap/pg_freespacemap.sql.in19
4 files changed, 365 insertions, 0 deletions
diff --git a/contrib/pg_freespacemap/Makefile b/contrib/pg_freespacemap/Makefile
new file mode 100644
index 0000000000..e0e5e0c76e
--- /dev/null
+++ b/contrib/pg_freespacemap/Makefile
@@ -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
diff --git a/contrib/pg_freespacemap/README.pg_freespacemap b/contrib/pg_freespacemap/README.pg_freespacemap
new file mode 100644
index 0000000000..d1711254c4
--- /dev/null
+++ b/contrib/pg_freespacemap/README.pg_freespacemap
@@ -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>
+
diff --git a/contrib/pg_freespacemap/pg_freespacemap.c b/contrib/pg_freespacemap/pg_freespacemap.c
new file mode 100644
index 0000000000..a3d9a6d31f
--- /dev/null
+++ b/contrib/pg_freespacemap/pg_freespacemap.c
@@ -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);
+
+}
diff --git a/contrib/pg_freespacemap/pg_freespacemap.sql.in b/contrib/pg_freespacemap/pg_freespacemap.sql.in
new file mode 100644
index 0000000000..a185186518
--- /dev/null
+++ b/contrib/pg_freespacemap/pg_freespacemap.sql.in
@@ -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;