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.
+ relfilenode | pg_class.relfilenode | Refilenode of the relation.
relblocknumber | | Offset of the page in the relation.
- blockfreebytes | | Free bytes in the block/page.
+ bytes | | Free bytes in the block/page, or NULL
+ | | for an index page (see below).
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.
+ The free space map can contain pages for btree indexes if they were emptied
+ by a vacuum process. The bytes field is set to NULL in this case.
+
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
-------------
regression=# \d pg_freespacemap
- View "public.pg_freespacemap"
+ View "public.pg_freespacemap"
Column | Type | Modifiers
- ---------------+---------+-----------
- blockid | integer |
- relfilenode | oid |
+ ----------------+---------+-----------
reltablespace | oid |
reldatabase | oid |
+ relfilenode | oid |
relblocknumber | bigint |
- blockfreebytes | integer |
+ bytes | 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);
+ SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.relblocknumber, p.bytes
+ FROM pg_freespacemap() p(reltablespace oid, reldatabase oid, relfilenode oid, relblocknumber bigint, bytes integer);
- regression=# SELECT c.relname, m.relblocknumber, m.blockfreebytes
+ regression=# SELECT c.relname, m.relblocknumber, m.bytes
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
+ relname | relblocknumber | bytes
+ ------------------------+----------------+--------
+ 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=#
* pg_freespacemap.c
* display some contents of the free space map.
*
- * $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.2 2006/02/14 15:03:59 tgl Exp $
+ * $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.3 2006/04/26 22:41:18 momjian Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "storage/freespace.h"
#include "utils/relcache.h"
-#define NUM_FREESPACE_PAGES_ELEM 6
+#define NUM_FREESPACE_PAGES_ELEM 5
#if defined(WIN32) || defined(__CYGWIN__)
/* Need DLLIMPORT for some things that are not so marked in main headers */
typedef struct
{
- uint32 blockid;
- uint32 relfilenode;
uint32 reltablespace;
uint32 reldatabase;
+ uint32 relfilenode;
uint32 relblocknumber;
- uint32 blockfreebytes;
+ uint32 bytes;
+ bool isindex;
} FreeSpacePagesRec;
/* 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",
+ TupleDescInitEntry(tupledesc, (AttrNumber) 1, "reltablespace",
OIDOID, -1, 0);
- TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
+ TupleDescInitEntry(tupledesc, (AttrNumber) 2, "reldatabase",
OIDOID, -1, 0);
- TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
+ TupleDescInitEntry(tupledesc, (AttrNumber) 3, "relfilenode",
OIDOID, -1, 0);
- TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relblocknumber",
+ TupleDescInitEntry(tupledesc, (AttrNumber) 4, "relblocknumber",
INT8OID, -1, 0);
- TupleDescInitEntry(tupledesc, (AttrNumber) 6, "blockfreebytes",
+ TupleDescInitEntry(tupledesc, (AttrNumber) 5, "bytes",
INT4OID, -1, 0);
/* Generate attribute metadata needed later to produce tuples */
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 */
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].relfilenode = fsmrel->key.relNode;
fctx->record[i].relblocknumber = IndexFSMPageGetPageNum(page);
- fctx->record[i].blockfreebytes = 0; /* index.*/
+ fctx->record[i].bytes = 0;
+ fctx->record[i].isindex = true;
page++;
i++;
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].relfilenode = fsmrel->key.relNode;
fctx->record[i].relblocknumber = FSMPageGetPageNum(page);
- fctx->record[i].blockfreebytes = FSMPageGetSpace(page);
+ fctx->record[i].bytes = FSMPageGetSpace(page);
+ fctx->record[i].isindex = false;
page++;
i++;
if (funcctx->call_cntr < funcctx->max_calls)
{
uint32 i = funcctx->call_cntr;
+ char *values[NUM_FREESPACE_PAGES_ELEM];
+ int j;
+ /*
+ * Use a temporary values array, initially pointing to fctx->values,
+ * so it can be reassigned w/o losing the storage for subsequent
+ * calls.
+ */
+ for (j = 0; j < NUM_FREESPACE_PAGES_ELEM; j++)
+ {
+ values[j] = fctx->values[j];
+ }
+
+
+ sprintf(values[0], "%u", fctx->record[i].reltablespace);
+ sprintf(values[1], "%u", fctx->record[i].reldatabase);
+ sprintf(values[2], "%u", fctx->record[i].relfilenode);
+ sprintf(values[3], "%u", fctx->record[i].relblocknumber);
- 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);
+ /*
+ * Set (free) bytes to NULL for an index relation.
+ */
+ if (fctx->record[i].isindex == true)
+ {
+ values[4] = NULL;
+ }
+ else
+ {
+ sprintf(values[4], "%u", fctx->record[i].bytes);
+ }
/* Build and return the tuple. */
- tuple = BuildTupleFromCStrings(funcctx->attinmeta, fctx->values);
+ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
result = HeapTupleGetDatum(tuple);