@ -15,7 +15,8 @@
# include "storage/bufmgr.h"
# define NUM_BUFFERCACHE_PAGES_ELEM 8
# define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
# define NUM_BUFFERCACHE_PAGES_ELEM 9
PG_MODULE_MAGIC ;
@ -33,6 +34,12 @@ typedef struct
bool isvalid ;
bool isdirty ;
uint16 usagecount ;
/*
* An int32 is sufficiently large , as MAX_BACKENDS prevents a buffer from
* being pinned by too many backends and each backend will only pin once
* because of bufmgr . c ' s PrivateRefCount array .
*/
int32 pinning_backends ;
} BufferCachePagesRec ;
@ -60,6 +67,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
MemoryContext oldcontext ;
BufferCachePagesContext * fctx ; /* User function context. */
TupleDesc tupledesc ;
TupleDesc expected_tupledesc ;
HeapTuple tuple ;
if ( SRF_IS_FIRSTCALL ( ) )
@ -75,8 +83,23 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
/* Create a user function context for cross-call persistence */
fctx = ( BufferCachePagesContext * ) palloc ( sizeof ( BufferCachePagesContext ) ) ;
/*
* To smoothly support upgrades from version 1.0 of this extension
* transparently handle the ( non - ) existance of the pinning_backends
* column . We unfortunately have to get the result type for that . . . -
* we can ' t use the result type determined by the function definition
* without potentially crashing when somebody uses the old ( or even
* wrong ) function definition though .
*/
if ( get_call_result_type ( fcinfo , NULL , & expected_tupledesc ) ! = TYPEFUNC_COMPOSITE )
elog ( ERROR , " return type must be a row type " ) ;
if ( expected_tupledesc - > natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM | |
expected_tupledesc - > natts > NUM_BUFFERCACHE_PAGES_ELEM )
elog ( ERROR , " incorrect number of output arguments " ) ;
/* Construct a tuple descriptor for the result rows. */
tupledesc = CreateTemplateTupleDesc ( NUM_BUFFERCACHE_PAGES_ELEM , false ) ;
tupledesc = CreateTemplateTupleDesc ( expected_tupledesc - > natts , false ) ;
TupleDescInitEntry ( tupledesc , ( AttrNumber ) 1 , " bufferid " ,
INT4OID , - 1 , 0 ) ;
TupleDescInitEntry ( tupledesc , ( AttrNumber ) 2 , " relfilenode " ,
@ -94,6 +117,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
TupleDescInitEntry ( tupledesc , ( AttrNumber ) 8 , " usage_count " ,
INT2OID , - 1 , 0 ) ;
if ( expected_tupledesc - > natts = = NUM_BUFFERCACHE_PAGES_ELEM )
TupleDescInitEntry ( tupledesc , ( AttrNumber ) 9 , " pinning_backends " ,
INT4OID , - 1 , 0 ) ;
fctx - > tupdesc = BlessTupleDesc ( tupledesc ) ;
/* Allocate NBuffers worth of BufferCachePagesRec records. */
@ -131,6 +158,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
fctx - > record [ i ] . forknum = bufHdr - > tag . forkNum ;
fctx - > record [ i ] . blocknum = bufHdr - > tag . blockNum ;
fctx - > record [ i ] . usagecount = bufHdr - > usage_count ;
fctx - > record [ i ] . pinning_backends = bufHdr - > refcount ;
if ( bufHdr - > flags & BM_DIRTY )
fctx - > record [ i ] . isdirty = true ;
@ -185,6 +213,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
nulls [ 5 ] = true ;
nulls [ 6 ] = true ;
nulls [ 7 ] = true ;
/* unused for v1.0 callers, but the array is always long enough */
nulls [ 8 ] = true ;
}
else
{
@ -202,6 +232,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
nulls [ 6 ] = false ;
values [ 7 ] = Int16GetDatum ( fctx - > record [ i ] . usagecount ) ;
nulls [ 7 ] = false ;
/* unused for v1.0 callers, but the array is always long enough */
values [ 8 ] = Int32GetDatum ( fctx - > record [ i ] . pinning_backends ) ;
nulls [ 8 ] = false ;
}
/* Build and return the tuple. */