|
| 1 | +/*------------------------------------------------------------------------- |
| 2 | + * |
| 3 | + * pg_freespacemap.c |
| 4 | + * display some contents of the free space map. |
| 5 | + * |
| 6 | + * $PostgreSQL: pgsql/contrib/pg_freespacemap/pg_freespacemap.c,v 1.1 2006/02/12 03:55:53 momjian Exp $ |
| 7 | + *------------------------------------------------------------------------- |
| 8 | + */ |
| 9 | +#include "postgres.h" |
| 10 | +#include "funcapi.h" |
| 11 | +#include "catalog/pg_type.h" |
| 12 | +#include "storage/freespace.h" |
| 13 | +#include "utils/relcache.h" |
| 14 | + |
| 15 | +#define NUM_FREESPACE_PAGES_ELEM 6 |
| 16 | + |
| 17 | +#if defined(WIN32) || defined(__CYGWIN__) |
| 18 | +extern DLLIMPORT volatile uint32 InterruptHoldoffCount; |
| 19 | +#endif |
| 20 | + |
| 21 | +Datum pg_freespacemap(PG_FUNCTION_ARGS); |
| 22 | + |
| 23 | + |
| 24 | +/* |
| 25 | + * Record structure holding the to be exposed free space data. |
| 26 | + */ |
| 27 | +typedef struct |
| 28 | +{ |
| 29 | + |
| 30 | + uint32 blockid; |
| 31 | + uint32 relfilenode; |
| 32 | + uint32 reltablespace; |
| 33 | + uint32 reldatabase; |
| 34 | + uint32 relblocknumber; |
| 35 | + uint32 blockfreebytes; |
| 36 | + |
| 37 | +} FreeSpacePagesRec; |
| 38 | + |
| 39 | + |
| 40 | +/* |
| 41 | + * Function context for data persisting over repeated calls. |
| 42 | + */ |
| 43 | +typedef struct |
| 44 | +{ |
| 45 | + |
| 46 | + AttInMetadata *attinmeta; |
| 47 | + FreeSpacePagesRec *record; |
| 48 | + char *values[NUM_FREESPACE_PAGES_ELEM]; |
| 49 | + |
| 50 | +} FreeSpacePagesContext; |
| 51 | + |
| 52 | + |
| 53 | +/* |
| 54 | + * Function returning data from the Free Space Map (FSM). |
| 55 | + */ |
| 56 | +PG_FUNCTION_INFO_V1(pg_freespacemap); |
| 57 | +Datum |
| 58 | +pg_freespacemap(PG_FUNCTION_ARGS) |
| 59 | +{ |
| 60 | + |
| 61 | + FuncCallContext *funcctx; |
| 62 | + Datum result; |
| 63 | + MemoryContext oldcontext; |
| 64 | + FreeSpacePagesContext *fctx; /* User function context. */ |
| 65 | + TupleDesc tupledesc; |
| 66 | + HeapTuple tuple; |
| 67 | + |
| 68 | + FSMHeader *FreeSpaceMap; /* FSM main structure. */ |
| 69 | + FSMRelation *fsmrel; /* Individual relation. */ |
| 70 | + |
| 71 | + |
| 72 | + if (SRF_IS_FIRSTCALL()) |
| 73 | + { |
| 74 | + uint32 i; |
| 75 | + uint32 numPages; /* Max possible no. of pages in map. */ |
| 76 | + int nPages; /* Mapped pages for a relation. */ |
| 77 | + |
| 78 | + /* |
| 79 | + * Get the free space map data structure. |
| 80 | + */ |
| 81 | + FreeSpaceMap = GetFreeSpaceMap(); |
| 82 | + |
| 83 | + numPages = MaxFSMPages; |
| 84 | + |
| 85 | + funcctx = SRF_FIRSTCALL_INIT(); |
| 86 | + |
| 87 | + /* Switch context when allocating stuff to be used in later calls */ |
| 88 | + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 89 | + |
| 90 | + /* Construct a tuple to return. */ |
| 91 | + tupledesc = CreateTemplateTupleDesc(NUM_FREESPACE_PAGES_ELEM, false); |
| 92 | + TupleDescInitEntry(tupledesc, (AttrNumber) 1, "blockid", |
| 93 | + INT4OID, -1, 0); |
| 94 | + TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode", |
| 95 | + OIDOID, -1, 0); |
| 96 | + TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace", |
| 97 | + OIDOID, -1, 0); |
| 98 | + TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase", |
| 99 | + OIDOID, -1, 0); |
| 100 | + TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relblocknumber", |
| 101 | + INT8OID, -1, 0); |
| 102 | + TupleDescInitEntry(tupledesc, (AttrNumber) 6, "blockfreebytes", |
| 103 | + INT4OID, -1, 0); |
| 104 | + |
| 105 | + /* Generate attribute metadata needed later to produce tuples */ |
| 106 | + funcctx->attinmeta = TupleDescGetAttInMetadata(tupledesc); |
| 107 | + |
| 108 | + /* |
| 109 | + * Create a function context for cross-call persistence and initialize |
| 110 | + * the counters. |
| 111 | + */ |
| 112 | + fctx = (FreeSpacePagesContext *) palloc(sizeof(FreeSpacePagesContext)); |
| 113 | + funcctx->user_fctx = fctx; |
| 114 | + |
| 115 | + /* Set an upper bound on the calls */ |
| 116 | + funcctx->max_calls = numPages; |
| 117 | + |
| 118 | + |
| 119 | + /* Allocate numPages worth of FreeSpacePagesRec records, this is also |
| 120 | + * an upper bound. |
| 121 | + */ |
| 122 | + fctx->record = (FreeSpacePagesRec *) palloc(sizeof(FreeSpacePagesRec) * numPages); |
| 123 | + |
| 124 | + /* allocate the strings for tuple formation */ |
| 125 | + fctx->values[0] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 126 | + fctx->values[1] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 127 | + fctx->values[2] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 128 | + fctx->values[3] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 129 | + fctx->values[4] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 130 | + fctx->values[5] = (char *) palloc(3 * sizeof(uint32) + 1); |
| 131 | + |
| 132 | + |
| 133 | + /* Return to original context when allocating transient memory */ |
| 134 | + MemoryContextSwitchTo(oldcontext); |
| 135 | + |
| 136 | + |
| 137 | + /* |
| 138 | + * Lock free space map and scan though all the relations, |
| 139 | + * for each relation, gets all its mapped pages. |
| 140 | + */ |
| 141 | + LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); |
| 142 | + |
| 143 | + |
| 144 | + i = 0; |
| 145 | + |
| 146 | + for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage) |
| 147 | + { |
| 148 | + |
| 149 | + if (fsmrel->isIndex) |
| 150 | + { /* Index relation. */ |
| 151 | + IndexFSMPageData *page; |
| 152 | + |
| 153 | + page = (IndexFSMPageData *) |
| 154 | + (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); |
| 155 | + |
| 156 | + for (nPages = 0; nPages < fsmrel->storedPages; nPages++) |
| 157 | + { |
| 158 | + |
| 159 | + fctx->record[i].blockid = i; |
| 160 | + fctx->record[i].relfilenode = fsmrel->key.relNode; |
| 161 | + fctx->record[i].reltablespace = fsmrel->key.spcNode; |
| 162 | + fctx->record[i].reldatabase = fsmrel->key.dbNode; |
| 163 | + fctx->record[i].relblocknumber = IndexFSMPageGetPageNum(page); |
| 164 | + fctx->record[i].blockfreebytes = 0; /* index.*/ |
| 165 | + |
| 166 | + page++; |
| 167 | + i++; |
| 168 | + } |
| 169 | + } |
| 170 | + else |
| 171 | + { /* Heap relation. */ |
| 172 | + FSMPageData *page; |
| 173 | + |
| 174 | + page = (FSMPageData *) |
| 175 | + (FreeSpaceMap->arena + fsmrel->firstChunk * CHUNKBYTES); |
| 176 | + |
| 177 | + for (nPages = 0; nPages < fsmrel->storedPages; nPages++) |
| 178 | + { |
| 179 | + fctx->record[i].blockid = i; |
| 180 | + fctx->record[i].relfilenode = fsmrel->key.relNode; |
| 181 | + fctx->record[i].reltablespace = fsmrel->key.spcNode; |
| 182 | + fctx->record[i].reldatabase = fsmrel->key.dbNode; |
| 183 | + fctx->record[i].relblocknumber = FSMPageGetPageNum(page); |
| 184 | + fctx->record[i].blockfreebytes = FSMPageGetSpace(page); |
| 185 | + |
| 186 | + page++; |
| 187 | + i++; |
| 188 | + } |
| 189 | + |
| 190 | + } |
| 191 | + |
| 192 | + } |
| 193 | + |
| 194 | + /* Set the real no. of calls as we know it now! */ |
| 195 | + funcctx->max_calls = i; |
| 196 | + |
| 197 | + /* Release free space map. */ |
| 198 | + LWLockRelease(FreeSpaceLock); |
| 199 | + } |
| 200 | + |
| 201 | + funcctx = SRF_PERCALL_SETUP(); |
| 202 | + |
| 203 | + /* Get the saved state */ |
| 204 | + fctx = funcctx->user_fctx; |
| 205 | + |
| 206 | + |
| 207 | + if (funcctx->call_cntr < funcctx->max_calls) |
| 208 | + { |
| 209 | + uint32 i = funcctx->call_cntr; |
| 210 | + |
| 211 | + |
| 212 | + sprintf(fctx->values[0], "%u", fctx->record[i].blockid); |
| 213 | + sprintf(fctx->values[1], "%u", fctx->record[i].relfilenode); |
| 214 | + sprintf(fctx->values[2], "%u", fctx->record[i].reltablespace); |
| 215 | + sprintf(fctx->values[3], "%u", fctx->record[i].reldatabase); |
| 216 | + sprintf(fctx->values[4], "%u", fctx->record[i].relblocknumber); |
| 217 | + sprintf(fctx->values[5], "%u", fctx->record[i].blockfreebytes); |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | + /* Build and return the tuple. */ |
| 222 | + tuple = BuildTupleFromCStrings(funcctx->attinmeta, fctx->values); |
| 223 | + result = HeapTupleGetDatum(tuple); |
| 224 | + |
| 225 | + |
| 226 | + SRF_RETURN_NEXT(funcctx, result); |
| 227 | + } |
| 228 | + else |
| 229 | + SRF_RETURN_DONE(funcctx); |
| 230 | + |
| 231 | +} |
0 commit comments