From 136bea1540e02dc8cdd8e1089808f96885a1751f Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 26 Apr 2006 22:46:09 +0000 Subject: [PATCH] Split out pg_freespace views to one for relations and another for pages, pg_freespacemap_relations and pg_freespacemap_pages. Mark Kirkwood --- .../pg_freespacemap/README.pg_freespacemap | 161 ++++++++++---- contrib/pg_freespacemap/pg_freespacemap.c | 208 +++++++++++++++++- .../pg_freespacemap/pg_freespacemap.sql.in | 32 ++- 3 files changed, 337 insertions(+), 64 deletions(-) diff --git a/contrib/pg_freespacemap/README.pg_freespacemap b/contrib/pg_freespacemap/README.pg_freespacemap index 8657acc27b..e4340b3bb1 100644 --- a/contrib/pg_freespacemap/README.pg_freespacemap +++ b/contrib/pg_freespacemap/README.pg_freespacemap @@ -1,15 +1,17 @@ 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. + This module consists of two C functions: 'pg_freespacemap_relations()' and + 'pg_freespacemap_pages()' that return a set of records, plus two views + 'pg_freespacemap_relations' and 'pg_freespacemap_pages' for more + user-friendly access to the functions. 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. + By default public access is REVOKED from the functions and views, just in + case there are security issues present in the code. Installation @@ -22,7 +24,7 @@ Installation $ gmake install - To register the functions: + To register the functions and views: $ psql -d -f pg_freespacemap.sql @@ -30,67 +32,130 @@ Installation Notes ----- - The definition of the columns exposed in the view is: + The definitions for the columns exposed in the views are: + + pg_freespacemap_relations Column | references | Description ----------------+----------------------+------------------------------------ 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. - bytes | | Free bytes in the block/page, or NULL + avgrequest | | Moving average of free space + | | requests. + lastpagecount | | Count of pages examined for useful + | | free space. + nextpage | | page index (from 0) to start next + | | search at. + + + pg_freespacemap_pages + + Column | references | Description + ----------------+----------------------+------------------------------------ + 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 | | Page offset in the relation. + bytes | | Free bytes in the page, or NULL | | for an index page (see below). - There is one row for each page in the free space map. + For pg_freespacemap_relations, there is one row for each relation in the free + space map. + + For pg_freespacemap_pages, 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. + Because the map is shared by all the databases, there are relations and 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. + The view 'freespacemap_pages' 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 + When either of the views are accessed, internal free space map locks are + taken, and a copy of the map data is made for them to display. + This ensures that the views produce 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. + could be some impact on database performance if they are read often. -Sample output +Sample output - pg_freespacemap_relations ------------- - regression=# \d pg_freespacemap - View "public.pg_freespacemap" - Column | Type | Modifiers - ----------------+---------+----------- - reltablespace | oid | - reldatabase | oid | - relfilenode | oid | - relblocknumber | bigint | - bytes | integer | - View definition: +regression=# \d pg_freespacemap_relations +View "public.pg_freespacemap_relations" + Column | Type | Modifiers +---------------+---------+----------- + reltablespace | oid | + reldatabase | oid | + relfilenode | oid | + avgrequest | bigint | + lastpagecount | integer | + nextpage | integer | +View definition: + SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.avgrequest, p.lastpagecount, p.nextpage + FROM pg_freespacemap_relations() p(reltablespace oid, reldatabase oid, relfilenode oid, avgrequest bigint, lastpagecount integer, nextpage integer); + +regression=# SELECT c.relname, r.avgrequest, r.lastpagecount, r.nextpage + FROM pg_freespacemap_relations r INNER JOIN pg_class c + ON c.relfilenode = r.relfilenode INNER JOIN pg_database d + ON r.reldatabase = d.oid AND (d.datname = current_database()) + ORDER BY c.relname LIMIT 10; + relname | avgrequest | lastpagecount | nextpage +--------------+------------+---------------+---------- + a_star | 250 | 1 | 0 + abstime_tbl | 249 | 1 | 0 + aggtest | 250 | 1 | 0 + altinhoid | 250 | 1 | 0 + altstartwith | 250 | 1 | 0 + arrtest | 254 | 1 | 0 + b_star | 250 | 1 | 0 + box_tbl | 250 | 1 | 0 + bt_f8_heap | 92 | 1 | 0 + bt_i4_heap | 94 | 1 | 0 +(10 rows) + +regression=# + + +Sample output - pg_freespacemap_pages +------------- + +regression=# \d pg_freespacemap_pages; + View "public.pg_freespacemap_pages" + Column | Type | Modifiers +----------------+---------+----------- + reltablespace | oid | + reldatabase | oid | + relfilenode | oid | + relblocknumber | bigint | + bytes | integer | +View definition: 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.bytes - FROM pg_freespacemap m INNER JOIN pg_class c - ON c.relfilenode = m.relfilenode LIMIT 10; - 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=# + FROM pg_freespacemap_pages() p(reltablespace oid, reldatabase oid, relfilenode oid, relblocknumber bigint, bytes integer); + +regression=# SELECT c.relname, p.relblocknumber, p.bytes + FROM pg_freespacemap_pages p INNER JOIN pg_class c + ON c.relfilenode = p.relfilenode INNER JOIN pg_database d + ON (p.reldatabase = d.oid AND d.datname = current_database()) + ORDER BY c.relname LIMIT 10; + relname | relblocknumber | bytes +--------------+----------------+------- + a_star | 0 | 8040 + abstime_tbl | 0 | 7908 + aggtest | 0 | 8008 + altinhoid | 0 | 8128 + altstartwith | 0 | 8128 + arrtest | 0 | 7172 + b_star | 0 | 7976 + box_tbl | 0 | 7912 + bt_f8_heap | 54 | 7728 + bt_i4_heap | 49 | 8008 +(10 rows) + +regression=# + Author diff --git a/contrib/pg_freespacemap/pg_freespacemap.c b/contrib/pg_freespacemap/pg_freespacemap.c index 7225be242c..4b298018fe 100644 --- a/contrib/pg_freespacemap/pg_freespacemap.c +++ b/contrib/pg_freespacemap/pg_freespacemap.c @@ -1,9 +1,9 @@ /*------------------------------------------------------------------------- * * pg_freespacemap.c - * display some contents of the free space map. + * display some contents of the free space relation and page maps. * - * $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.3 2006/04/26 22:41:18 momjian Exp $ + * $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.4 2006/04/26 22:46:09 momjian Exp $ *------------------------------------------------------------------------- */ #include "postgres.h" @@ -13,18 +13,21 @@ #include "utils/relcache.h" #define NUM_FREESPACE_PAGES_ELEM 5 +#define NUM_FREESPACE_RELATIONS_ELEM 6 #if defined(WIN32) || defined(__CYGWIN__) /* Need DLLIMPORT for some things that are not so marked in main headers */ extern DLLIMPORT int MaxFSMPages; +extern DLLIMPORT int MaxFSMRelations; extern DLLIMPORT volatile uint32 InterruptHoldoffCount; #endif -Datum pg_freespacemap(PG_FUNCTION_ARGS); +Datum pg_freespacemap_pages(PG_FUNCTION_ARGS); +Datum pg_freespacemap_relations(PG_FUNCTION_ARGS); /* - * Record structure holding the to be exposed free space data. + * Record structure holding the to be exposed free space page data. */ typedef struct { @@ -40,7 +43,24 @@ typedef struct /* - * Function context for data persisting over repeated calls. + * Record structure holding the to be exposed free space relation data. + */ +typedef struct +{ + + uint32 reltablespace; + uint32 reldatabase; + uint32 relfilenode; + int64 avgrequest; + int lastpagecount; + int nextpage; + +} FreeSpaceRelationsRec; + + + +/* + * Function context for page data persisting over repeated calls. */ typedef struct { @@ -53,11 +73,24 @@ typedef struct /* - * Function returning data from the Free Space Map (FSM). + * Function context for relation data persisting over repeated calls. + */ +typedef struct +{ + + AttInMetadata *attinmeta; + FreeSpaceRelationsRec *record; + char *values[NUM_FREESPACE_RELATIONS_ELEM]; + +} FreeSpaceRelationsContext; + + +/* + * Function returning page data from the Free Space Map (FSM). */ -PG_FUNCTION_INFO_V1(pg_freespacemap); +PG_FUNCTION_INFO_V1(pg_freespacemap_pages); Datum -pg_freespacemap(PG_FUNCTION_ARGS) +pg_freespacemap_pages(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; @@ -250,3 +283,162 @@ pg_freespacemap(PG_FUNCTION_ARGS) SRF_RETURN_DONE(funcctx); } + + +/* + * Function returning relation data from the Free Space Map (FSM). + */ +PG_FUNCTION_INFO_V1(pg_freespacemap_relations); +Datum +pg_freespacemap_relations(PG_FUNCTION_ARGS) +{ + + FuncCallContext *funcctx; + Datum result; + MemoryContext oldcontext; + FreeSpaceRelationsContext *fctx; /* User function context. */ + TupleDesc tupledesc; + HeapTuple tuple; + + FSMHeader *FreeSpaceMap; /* FSM main structure. */ + FSMRelation *fsmrel; /* Individual relation. */ + + + if (SRF_IS_FIRSTCALL()) + { + uint32 i; + uint32 numRelations; /* Max no. of Relations in map. */ + + /* + * Get the free space map data structure. + */ + FreeSpaceMap = GetFreeSpaceMap(); + + numRelations = MaxFSMRelations; + + 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_RELATIONS_ELEM, false); + TupleDescInitEntry(tupledesc, (AttrNumber) 1, "reltablespace", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 2, "reldatabase", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 3, "relfilenode", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 4, "avgrequest", + INT8OID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 5, "lastpageCount", + INT4OID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 6, "nextpage", + 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 = (FreeSpaceRelationsContext *) palloc(sizeof(FreeSpaceRelationsContext)); + funcctx->user_fctx = fctx; + + /* Set an upper bound on the calls */ + funcctx->max_calls = numRelations; + + + /* Allocate numRelations worth of FreeSpaceRelationsRec records, + * this is also an upper bound. + */ + fctx->record = (FreeSpaceRelationsRec *) palloc(sizeof(FreeSpaceRelationsRec) * numRelations); + + /* 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(int64) + 1); + fctx->values[4] = (char *) palloc(3 * sizeof(int32) + 1); + fctx->values[5] = (char *) palloc(3 * sizeof(int32) + 1); + + + /* Return to original context when allocating transient memory */ + MemoryContextSwitchTo(oldcontext); + + + /* + * Lock free space map and scan though all the relations, + */ + LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); + + + i = 0; + + for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage) + { + + fctx->record[i].reltablespace = fsmrel->key.spcNode; + fctx->record[i].reldatabase = fsmrel->key.dbNode; + fctx->record[i].relfilenode = fsmrel->key.relNode; + fctx->record[i].avgrequest = (int64)fsmrel->avgRequest; + fctx->record[i].lastpagecount = fsmrel->lastPageCount; + fctx->record[i].nextpage = fsmrel->nextPage; + + 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; + char *values[NUM_FREESPACE_RELATIONS_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_RELATIONS_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], INT64_FORMAT, fctx->record[i].avgrequest); + sprintf(values[4], "%d", fctx->record[i].lastpagecount); + sprintf(values[5], "%d", fctx->record[i].nextpage); + + + + /* Build and return the tuple. */ + tuple = BuildTupleFromCStrings(funcctx->attinmeta, 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 index b69026953f..ff0619bd9a 100644 --- a/contrib/pg_freespacemap/pg_freespacemap.sql.in +++ b/contrib/pg_freespacemap/pg_freespacemap.sql.in @@ -2,18 +2,34 @@ BEGIN; SET search_path = public; --- Register the function. -CREATE OR REPLACE FUNCTION pg_freespacemap() + +-- Register the functions. +CREATE OR REPLACE FUNCTION pg_freespacemap_pages() RETURNS SETOF RECORD -AS 'MODULE_PATHNAME', 'pg_freespacemap' +AS 'MODULE_PATHNAME', 'pg_freespacemap_pages' LANGUAGE C; --- Create a view for convenient access. -CREATE VIEW pg_freespacemap AS - SELECT P.* FROM pg_freespacemap() AS P +CREATE OR REPLACE FUNCTION pg_freespacemap_relations() +RETURNS SETOF RECORD +AS 'MODULE_PATHNAME', 'pg_freespacemap_relations' +LANGUAGE C; + + +-- Create views for convenient access. +CREATE VIEW pg_freespacemap_pages AS + SELECT P.* FROM pg_freespacemap_pages() AS P (reltablespace oid, reldatabase oid, relfilenode oid, relblocknumber int8, bytes int4); +CREATE VIEW pg_freespacemap_relations AS + SELECT P.* FROM pg_freespacemap_relations() AS P + (reltablespace oid, reldatabase oid, relfilenode oid, avgrequest int8, lastpagecount integer, nextpage integer); + + -- Don't want these to be available at public. -REVOKE ALL ON FUNCTION pg_freespacemap() FROM PUBLIC; -REVOKE ALL ON pg_freespacemap FROM PUBLIC; +REVOKE ALL ON FUNCTION pg_freespacemap_pages() FROM PUBLIC; +REVOKE ALL ON pg_freespacemap_pages FROM PUBLIC; + +REVOKE ALL ON FUNCTION pg_freespacemap_relations() FROM PUBLIC; +REVOKE ALL ON pg_freespacemap_relations FROM PUBLIC; + COMMIT; -- GitLab