00001
00002
00003
00004
00005
00006
00007
00008
00009 #include "postgres.h"
00010
00011 #include "access/htup_details.h"
00012 #include "catalog/pg_type.h"
00013 #include "funcapi.h"
00014 #include "storage/buf_internals.h"
00015 #include "storage/bufmgr.h"
00016
00017
00018 #define NUM_BUFFERCACHE_PAGES_ELEM 8
00019
00020 PG_MODULE_MAGIC;
00021
00022 Datum pg_buffercache_pages(PG_FUNCTION_ARGS);
00023
00024
00025
00026
00027
00028 typedef struct
00029 {
00030 uint32 bufferid;
00031 Oid relfilenode;
00032 Oid reltablespace;
00033 Oid reldatabase;
00034 ForkNumber forknum;
00035 BlockNumber blocknum;
00036 bool isvalid;
00037 bool isdirty;
00038 uint16 usagecount;
00039 } BufferCachePagesRec;
00040
00041
00042
00043
00044
00045 typedef struct
00046 {
00047 TupleDesc tupdesc;
00048 BufferCachePagesRec *record;
00049 } BufferCachePagesContext;
00050
00051
00052
00053
00054
00055
00056 PG_FUNCTION_INFO_V1(pg_buffercache_pages);
00057
00058 Datum
00059 pg_buffercache_pages(PG_FUNCTION_ARGS)
00060 {
00061 FuncCallContext *funcctx;
00062 Datum result;
00063 MemoryContext oldcontext;
00064 BufferCachePagesContext *fctx;
00065 TupleDesc tupledesc;
00066 HeapTuple tuple;
00067
00068 if (SRF_IS_FIRSTCALL())
00069 {
00070 int i;
00071 volatile BufferDesc *bufHdr;
00072
00073 funcctx = SRF_FIRSTCALL_INIT();
00074
00075
00076 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
00077
00078
00079 fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext));
00080
00081
00082 tupledesc = CreateTemplateTupleDesc(NUM_BUFFERCACHE_PAGES_ELEM, false);
00083 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
00084 INT4OID, -1, 0);
00085 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
00086 OIDOID, -1, 0);
00087 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
00088 OIDOID, -1, 0);
00089 TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
00090 OIDOID, -1, 0);
00091 TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
00092 INT2OID, -1, 0);
00093 TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
00094 INT8OID, -1, 0);
00095 TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
00096 BOOLOID, -1, 0);
00097 TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
00098 INT2OID, -1, 0);
00099
00100 fctx->tupdesc = BlessTupleDesc(tupledesc);
00101
00102
00103 fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NBuffers);
00104
00105
00106 funcctx->max_calls = NBuffers;
00107 funcctx->user_fctx = fctx;
00108
00109
00110 MemoryContextSwitchTo(oldcontext);
00111
00112
00113
00114
00115
00116
00117
00118 for (i = 0; i < NUM_BUFFER_PARTITIONS; i++)
00119 LWLockAcquire(FirstBufMappingLock + i, LW_SHARED);
00120
00121
00122
00123
00124
00125 for (i = 0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++)
00126 {
00127
00128 LockBufHdr(bufHdr);
00129
00130 fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
00131 fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
00132 fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
00133 fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
00134 fctx->record[i].forknum = bufHdr->tag.forkNum;
00135 fctx->record[i].blocknum = bufHdr->tag.blockNum;
00136 fctx->record[i].usagecount = bufHdr->usage_count;
00137
00138 if (bufHdr->flags & BM_DIRTY)
00139 fctx->record[i].isdirty = true;
00140 else
00141 fctx->record[i].isdirty = false;
00142
00143
00144 if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_TAG_VALID))
00145 fctx->record[i].isvalid = true;
00146 else
00147 fctx->record[i].isvalid = false;
00148
00149 UnlockBufHdr(bufHdr);
00150 }
00151
00152
00153
00154
00155
00156
00157
00158
00159 for (i = NUM_BUFFER_PARTITIONS; --i >= 0;)
00160 LWLockRelease(FirstBufMappingLock + i);
00161 }
00162
00163 funcctx = SRF_PERCALL_SETUP();
00164
00165
00166 fctx = funcctx->user_fctx;
00167
00168 if (funcctx->call_cntr < funcctx->max_calls)
00169 {
00170 uint32 i = funcctx->call_cntr;
00171 Datum values[NUM_BUFFERCACHE_PAGES_ELEM];
00172 bool nulls[NUM_BUFFERCACHE_PAGES_ELEM];
00173
00174 values[0] = Int32GetDatum(fctx->record[i].bufferid);
00175 nulls[0] = false;
00176
00177
00178
00179
00180
00181 if (fctx->record[i].blocknum == InvalidBlockNumber ||
00182 fctx->record[i].isvalid == false)
00183 {
00184 nulls[1] = true;
00185 nulls[2] = true;
00186 nulls[3] = true;
00187 nulls[4] = true;
00188 nulls[5] = true;
00189 nulls[6] = true;
00190 nulls[7] = true;
00191 }
00192 else
00193 {
00194 values[1] = ObjectIdGetDatum(fctx->record[i].relfilenode);
00195 nulls[1] = false;
00196 values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
00197 nulls[2] = false;
00198 values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
00199 nulls[3] = false;
00200 values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
00201 nulls[4] = false;
00202 values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
00203 nulls[5] = false;
00204 values[6] = BoolGetDatum(fctx->record[i].isdirty);
00205 nulls[6] = false;
00206 values[7] = Int16GetDatum(fctx->record[i].usagecount);
00207 nulls[7] = false;
00208 }
00209
00210
00211 tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
00212 result = HeapTupleGetDatum(tuple);
00213
00214 SRF_RETURN_NEXT(funcctx, result);
00215 }
00216 else
00217 SRF_RETURN_DONE(funcctx);
00218 }