Code review for contrib/pg_freespacemap. Add a storedpages column to
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 May 2006 20:39:34 +0000 (20:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 4 May 2006 20:39:34 +0000 (20:39 +0000)
pg_freespacemap_relations --- while one could theoretically get that
number by counting rows in pg_freespacemap_pages, it's surely the hard
way to do it.  Avoid expensive and inconvenient conversion to and from
text format.  Minor code and docs cleanup.

contrib/pg_freespacemap/README.pg_freespacemap
contrib/pg_freespacemap/pg_freespacemap.c
contrib/pg_freespacemap/pg_freespacemap.sql.in

index e4340b3bb13500d67f480ac8e85c2621df85c102..d66bd20329402fe157e6cff54a89a7790abeb193 100644 (file)
@@ -39,13 +39,15 @@ Notes
        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.
+   reldatabase    | pg_database.oid      | Database oid of the relation.
+   relfilenode    | pg_class.relfilenode | Relfilenode of the relation.
    avgrequest     |                      | Moving average of free space 
-                  |                      | requests.
-   lastpagecount  |                      | Count of pages examined for useful
-                  |                      | free space.
-   nextpage       |                      | page index (from 0) to start next 
+                  |                      | requests (NULL for indexes)
+   lastpagecount  |                      | Count of pages last reported as
+                  |                      | containing useful free space.
+   storedpages    |                      | Count of pages actually stored
+                  |                      | in free space map.
+   nextpage       |                      | Page index (from 0) to start next 
                   |                      | search at.
 
 
@@ -54,24 +56,33 @@ Notes
        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.
+   reldatabase    | pg_database.oid      | Database oid of the relation.
+   relfilenode    | pg_class.relfilenode | Relfilenode of the relation.
+   relblocknumber |                      | Page number in the relation.
    bytes          |                      | Free bytes in the page, or NULL
                   |                      | for an index page (see below).
 
 
   For pg_freespacemap_relations, there is one row for each relation in the free
-  space map.
+  space map.  storedpages is the number of pages actually stored in the map,
+  while lastpagecount is the number of pages VACUUM last tried to store
+  (ie, the number that VACUUM thought had useful amounts of free space).
+
+  If storedpages is consistently less than lastpagecount then it'd be a good
+  idea to increase max_fsm_pages.  Also, if the number of rows in
+  pg_freespacemap_relations is close to max_fsm_relations, then you should
+  consider increasing max_fsm_relations.
 
   For pg_freespacemap_pages, there is one row for each page in the free space 
-  map.
+  map.  The number of rows for a relation will match the storedpages column
+  in pg_freespacemap_relations.
 
-  Because the map is shared by all the databases, there are relations and pages
-  from relations not belonging to the current database.
+  For indexes, what is tracked is entirely-unused pages, rather than free
+  space within pages.  Therefore, the average request size and free bytes
+  within a page are not meaningful, and are shown as NULL.
 
-  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.
+  Because the map is shared by all the databases, it will include relations
+  not belonging to the current database.
 
   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. 
@@ -85,44 +96,43 @@ Sample output - pg_freespacemap_relations
 
 regression=# \d pg_freespacemap_relations
 View "public.pg_freespacemap_relations"
-    Column     |  Type   | Modifiers 
+    Column     |  Type   | Modifiers
 ---------------+---------+-----------
- reltablespace | oid     | 
- reldatabase   | oid     | 
- relfilenode   | oid     | 
- avgrequest    | bigint  | 
- lastpagecount | integer | 
- nextpage      | integer | 
+ reltablespace | oid     |
+ reldatabase   | oid     |
+ relfilenode   | oid     |
+ avgrequest    | integer |
+ lastpagecount | integer |
+ storedpages   | 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);
+ SELECT p.reltablespace, p.reldatabase, p.relfilenode, p.avgrequest, p.lastpagecount, p.storedpages, p.nextpage
+   FROM pg_freespacemap_relations() p(reltablespace oid, reldatabase oid, relfilenode oid, avgrequest integer, lastpagecount integer, storedpages integer, nextpage integer);
 
-regression=# SELECT c.relname, r.avgrequest, r.lastpagecount, r.nextpage
+regression=# SELECT c.relname, r.avgrequest, r.lastpagecount, r.storedpages
              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
+             ORDER BY r.storedpages DESC LIMIT 10;
+             relname             | avgrequest | lastpagecount | storedpages
+---------------------------------+------------+---------------+-------------
+ onek                            |        256 |           109 |         109
+ pg_attribute                    |        167 |            93 |          93
+ pg_class                        |        191 |            49 |          49
+ pg_attribute_relid_attnam_index |            |            48 |          48
+ onek2                           |        256 |            37 |          37
+ pg_depend                       |         95 |            26 |          26
+ pg_type                         |        199 |            16 |          16
+ pg_rewrite                      |       1011 |            13 |          13
pg_class_relname_nsp_index      |            |            10 |          10
+ pg_proc                         |        302 |             8 |           8
 (10 rows)
 
-regression=# 
-
 
 Sample output - pg_freespacemap_pages
 -------------
 
-regression=# \d pg_freespacemap_pages;
+regression=# \d pg_freespacemap_pages
  View "public.pg_freespacemap_pages"
      Column     |  Type   | Modifiers 
 ----------------+---------+-----------
@@ -154,8 +164,6 @@ regression=# SELECT c.relname, p.relblocknumber, p.bytes
  bt_i4_heap   |             49 |  8008
 (10 rows)
 
-regression=# 
-
 
 
 Author
index 4b298018fe9cdf21e5394fff3b0f1ddc7327908d..5ee85fdddd5932aac76810456891b2c18d3c3747 100644 (file)
@@ -3,17 +3,19 @@
  * pg_freespacemap.c
  *       display some contents of the free space relation and page maps.
  *
- *       $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.4 2006/04/26 22:46:09 momjian Exp $
+ *       $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.5 2006/05/04 20:39:34 tgl Exp $
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
+
 #include "funcapi.h"
+#include "access/heapam.h"
 #include "catalog/pg_type.h"
 #include "storage/freespace.h"
-#include "utils/relcache.h"
+
 
 #define                NUM_FREESPACE_PAGES_ELEM        5
-#define                NUM_FREESPACE_RELATIONS_ELEM    6
+#define                NUM_FREESPACE_RELATIONS_ELEM    7
 
 #if defined(WIN32) || defined(__CYGWIN__)
 /* Need DLLIMPORT for some things that are not so marked in main headers */
@@ -27,34 +29,32 @@ Datum               pg_freespacemap_relations(PG_FUNCTION_ARGS);
 
 
 /*
- * Record structure holding the to be exposed free space page data.
+ * Record structure holding the to be exposed per-page data.
  */
 typedef struct
 {
-
-       uint32                          reltablespace;
-       uint32                          reldatabase;
-       uint32                          relfilenode;
-       uint32                          relblocknumber;
-       uint32                          bytes;
-       bool                            isindex;
-
+       Oid                             reltablespace;
+       Oid                             reldatabase;
+       Oid                             relfilenode;
+       BlockNumber             relblocknumber;
+       Size                    bytes;
+       bool                    isindex;
 }      FreeSpacePagesRec;
 
 
 /*
- * Record structure holding the to be exposed free space relation data.
+ * Record structure holding the to be exposed per-relation data.
  */
 typedef struct
 {
-
-       uint32                          reltablespace;
-       uint32                          reldatabase;
-       uint32                          relfilenode;
-       int64                           avgrequest;
-       int                                     lastpagecount;
-       int                                     nextpage;
-
+       Oid                             reltablespace;
+       Oid                             reldatabase;
+       Oid                             relfilenode;
+       Size                    avgrequest;
+       int                             lastpagecount;
+       int                             storedpages;
+       int                             nextpage;
+       bool                    isindex;
 }      FreeSpaceRelationsRec;
 
 
@@ -64,11 +64,8 @@ typedef struct
  */
 typedef struct
 {
-
-       AttInMetadata           *attinmeta;
+       TupleDesc                       tupdesc;
        FreeSpacePagesRec       *record;
-       char                            *values[NUM_FREESPACE_PAGES_ELEM];
-
 }      FreeSpacePagesContext;
 
 
@@ -77,11 +74,8 @@ typedef struct
  */
 typedef struct
 {
-
-       AttInMetadata           *attinmeta;
+       TupleDesc                       tupdesc;
        FreeSpaceRelationsRec   *record;
-       char                            *values[NUM_FREESPACE_RELATIONS_ELEM];
-
 }      FreeSpaceRelationsContext;
 
 
@@ -89,26 +83,24 @@ typedef struct
  * Function returning page data from the Free Space Map (FSM).
  */
 PG_FUNCTION_INFO_V1(pg_freespacemap_pages);
+
 Datum
 pg_freespacemap_pages(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. */
+               int                             i;
+               int                             numPages;       /* Max possible no. of pages in map. */
+               int                             nPages;         /* Mapped pages for a relation. */
                
                /*
                 * Get the free space map data structure.
@@ -122,7 +114,13 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
                /* Switch context when allocating stuff to be used in later calls */
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-               /* Construct a tuple to return. */
+               /*
+                * Create a function context for cross-call persistence.
+                */
+               fctx = (FreeSpacePagesContext *) palloc(sizeof(FreeSpacePagesContext));
+               funcctx->user_fctx = fctx;
+
+               /* Construct a tuple descriptor for the result rows. */
                tupledesc = CreateTemplateTupleDesc(NUM_FREESPACE_PAGES_ELEM, false);
                TupleDescInitEntry(tupledesc, (AttrNumber) 1, "reltablespace",
                                                   OIDOID, -1, 0);
@@ -135,59 +133,37 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
                TupleDescInitEntry(tupledesc, (AttrNumber) 5, "bytes",
                                                   INT4OID, -1, 0);
 
-               /* Generate attribute metadata needed later to produce tuples */
-               funcctx->attinmeta = TupleDescGetAttInMetadata(tupledesc);
+               fctx->tupdesc = BlessTupleDesc(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
+                * Allocate numPages worth of FreeSpacePagesRec records, this is
                 * 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);
-
-
                /* 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.
+                * 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. */
+                       {
+                               /* Index relation. */
                                IndexFSMPageData *page;
 
                                page = (IndexFSMPageData *)
-                                               (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
+                                       (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
 
                                for (nPages = 0; nPages < fsmrel->storedPages; nPages++)
                                {
-
                                        fctx->record[i].reltablespace = fsmrel->key.spcNode;
                                        fctx->record[i].reldatabase = fsmrel->key.dbNode;
                                        fctx->record[i].relfilenode = fsmrel->key.relNode;
@@ -200,11 +176,12 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
                                }
                        }
                        else
-                       {       /* Heap relation. */
+                       {
+                               /* Heap relation. */
                                FSMPageData *page;
 
                                page = (FSMPageData *)
-                                               (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
+                                       (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES);
 
                                for (nPages = 0; nPages < fsmrel->storedPages; nPages++)
                                {
@@ -218,16 +195,15 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
                                        page++;
                                        i++;
                                }
-                       
                        }
-
                }
 
-               /* Set the real no. of calls as we know it now! */
-               funcctx->max_calls = i;
-
                /* Release free space map. */
                LWLockRelease(FreeSpaceLock);
+
+               /* Set the real no. of calls as we know it now! */
+               Assert(i <= numPages);
+               funcctx->max_calls = i;
        }
 
        funcctx = SRF_PERCALL_SETUP();
@@ -235,53 +211,43 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
        /* Get the saved state */
        fctx = funcctx->user_fctx;
 
-
        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);
-
+               int                     i = funcctx->call_cntr;
+               FreeSpacePagesRec       *record = &fctx->record[i];
+               Datum           values[NUM_FREESPACE_PAGES_ELEM];
+               bool            nulls[NUM_FREESPACE_PAGES_ELEM];
+
+               values[0] = ObjectIdGetDatum(record->reltablespace);
+               nulls[0] = false;
+               values[1] = ObjectIdGetDatum(record->reldatabase);
+               nulls[1] = false;
+               values[2] = ObjectIdGetDatum(record->relfilenode);
+               nulls[2] = false;
+               values[3] = Int64GetDatum((int64) record->relblocknumber);
+               nulls[3] = false;
 
                /*
                 * Set (free) bytes to NULL for an index relation.
                 */
-               if (fctx->record[i].isindex == true)
+               if (record->isindex)
                {
-                       values[4] = NULL;
+                       nulls[4] = true;
                }
                else
                {
-                       sprintf(values[4], "%u", fctx->record[i].bytes);
+                       values[4] = UInt32GetDatum(record->bytes);
+                       nulls[4] = false;
                }
 
-
                /* Build and return the tuple. */
-               tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+               tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
                result = HeapTupleGetDatum(tuple);
 
-
                SRF_RETURN_NEXT(funcctx, result);
        }
        else
                SRF_RETURN_DONE(funcctx);
-
 }
 
 
@@ -289,25 +255,23 @@ pg_freespacemap_pages(PG_FUNCTION_ARGS)
  * 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. */
+       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. */
+               int                             i;
+               int                             numRelations;   /* Max no. of Relations in map. */
                
                /*
                 * Get the free space map data structure.
@@ -321,7 +285,13 @@ pg_freespacemap_relations(PG_FUNCTION_ARGS)
                /* Switch context when allocating stuff to be used in later calls */
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-               /* Construct a tuple to return. */
+               /*
+                * Create a function context for cross-call persistence.
+                */
+               fctx = (FreeSpaceRelationsContext *) palloc(sizeof(FreeSpaceRelationsContext));
+               funcctx->user_fctx = fctx;
+
+               /* Construct a tuple descriptor for the result rows. */
                tupledesc = CreateTemplateTupleDesc(NUM_FREESPACE_RELATIONS_ELEM, false);
                TupleDescInitEntry(tupledesc, (AttrNumber) 1, "reltablespace",
                                                   OIDOID, -1, 0);
@@ -330,72 +300,52 @@ pg_freespacemap_relations(PG_FUNCTION_ARGS)
                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",
+               TupleDescInitEntry(tupledesc, (AttrNumber) 5, "lastpagecount",
+                                                  INT4OID, -1, 0);
+               TupleDescInitEntry(tupledesc, (AttrNumber) 6, "storedpages",
+                                                  INT4OID, -1, 0);
+               TupleDescInitEntry(tupledesc, (AttrNumber) 7, "nextpage",
                                                   INT4OID, -1, 0);
 
-               /* Generate attribute metadata needed later to produce tuples */
-               funcctx->attinmeta = TupleDescGetAttInMetadata(tupledesc);
+               fctx->tupdesc = BlessTupleDesc(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, 
+                * 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
+                * 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].storedpages = fsmrel->storedPages;
                        fctx->record[i].nextpage = fsmrel->nextPage;
+                       fctx->record[i].isindex = fsmrel->isIndex;
 
                        i++;
-
-
                }
 
-               /* Set the real no. of calls as we know it now! */
-               funcctx->max_calls = i;
-
                /* Release free space map. */
                LWLockRelease(FreeSpaceLock);
+
+               /* Set the real no. of calls as we know it now! */
+               Assert(i <= numRelations);
+               funcctx->max_calls = i;
        }
 
        funcctx = SRF_PERCALL_SETUP();
@@ -403,42 +353,44 @@ pg_freespacemap_relations(PG_FUNCTION_ARGS)
        /* 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;
-
+               int                     i = funcctx->call_cntr;
+               FreeSpaceRelationsRec   *record = &fctx->record[i];
+               Datum           values[NUM_FREESPACE_RELATIONS_ELEM];
+               bool            nulls[NUM_FREESPACE_RELATIONS_ELEM];
+
+               values[0] = ObjectIdGetDatum(record->reltablespace);
+               nulls[0] = false;
+               values[1] = ObjectIdGetDatum(record->reldatabase);
+               nulls[1] = false;
+               values[2] = ObjectIdGetDatum(record->relfilenode);
+               nulls[2] = false;
                /*
-                * Use a temporary values array, initially pointing to fctx->values,
-                * so it can be reassigned w/o losing the storage for subsequent
-                * calls.
+                * avgrequest isn't meaningful for an index
                 */
-               for (j = 0; j < NUM_FREESPACE_RELATIONS_ELEM; j++)
+               if (record->isindex)
                {
-                       values[j] = fctx->values[j];
+                       nulls[3] = true;
                }
-
-
-               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);
-
-
+               else
+               {
+                       values[3] = UInt32GetDatum(record->avgrequest);
+                       nulls[3] = false;
+               }
+               values[4] = Int32GetDatum(record->lastpagecount);
+               nulls[4] = false;
+               values[5] = Int32GetDatum(record->storedpages);
+               nulls[5] = false;
+               values[6] = Int32GetDatum(record->nextpage);
+               nulls[6] = false;
 
                /* Build and return the tuple. */
-               tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+               tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
                result = HeapTupleGetDatum(tuple);
 
-
                SRF_RETURN_NEXT(funcctx, result);
        }
        else
                SRF_RETURN_DONE(funcctx);
-
-
 }
index ff0619bd9ac7c6d05204dce396976588a8d560ed..c45335e3b1e628cd7e677d81de198ef7f86f154e 100644 (file)
@@ -18,14 +18,24 @@ 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);
+       (reltablespace oid,
+        reldatabase oid,
+        relfilenode oid,
+        relblocknumber bigint,
+        bytes integer);
  
 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);
+       (reltablespace oid,
+        reldatabase oid,
+        relfilenode oid,
+        avgrequest integer,
+        lastpagecount integer,
+        storedpages integer,
+        nextpage integer);
 
  
--- Don't want these to be available at public.
+-- Don't want these to be available to public.
 REVOKE ALL ON FUNCTION pg_freespacemap_pages() FROM PUBLIC;
 REVOKE ALL ON pg_freespacemap_pages FROM PUBLIC;