15
15
#include "storage/bufmgr.h"
16
16
17
17
18
- #define NUM_BUFFERCACHE_PAGES_ELEM 8
18
+ #define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
19
+ #define NUM_BUFFERCACHE_PAGES_ELEM 9
19
20
20
21
PG_MODULE_MAGIC ;
21
22
@@ -33,6 +34,12 @@ typedef struct
33
34
bool isvalid ;
34
35
bool isdirty ;
35
36
uint16 usagecount ;
37
+ /*
38
+ * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
39
+ * being pinned by too many backends and each backend will only pin once
40
+ * because of bufmgr.c's PrivateRefCount array.
41
+ */
42
+ int32 pinning_backends ;
36
43
} BufferCachePagesRec ;
37
44
38
45
@@ -60,6 +67,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
60
67
MemoryContext oldcontext ;
61
68
BufferCachePagesContext * fctx ; /* User function context. */
62
69
TupleDesc tupledesc ;
70
+ TupleDesc expected_tupledesc ;
63
71
HeapTuple tuple ;
64
72
65
73
if (SRF_IS_FIRSTCALL ())
@@ -75,8 +83,23 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
75
83
/* Create a user function context for cross-call persistence */
76
84
fctx = (BufferCachePagesContext * ) palloc (sizeof (BufferCachePagesContext ));
77
85
86
+ /*
87
+ * To smoothly support upgrades from version 1.0 of this extension
88
+ * transparently handle the (non-)existance of the pinning_backends
89
+ * column. We unfortunately have to get the result type for that... -
90
+ * we can't use the result type determined by the function definition
91
+ * without potentially crashing when somebody uses the old (or even
92
+ * wrong) function definition though.
93
+ */
94
+ if (get_call_result_type (fcinfo , NULL , & expected_tupledesc ) != TYPEFUNC_COMPOSITE )
95
+ elog (ERROR , "return type must be a row type" );
96
+
97
+ if (expected_tupledesc -> natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
98
+ expected_tupledesc -> natts > NUM_BUFFERCACHE_PAGES_ELEM )
99
+ elog (ERROR , "incorrect number of output arguments" );
100
+
78
101
/* Construct a tuple descriptor for the result rows. */
79
- tupledesc = CreateTemplateTupleDesc (NUM_BUFFERCACHE_PAGES_ELEM , false);
102
+ tupledesc = CreateTemplateTupleDesc (expected_tupledesc -> natts , false);
80
103
TupleDescInitEntry (tupledesc , (AttrNumber ) 1 , "bufferid" ,
81
104
INT4OID , -1 , 0 );
82
105
TupleDescInitEntry (tupledesc , (AttrNumber ) 2 , "relfilenode" ,
@@ -94,6 +117,10 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
94
117
TupleDescInitEntry (tupledesc , (AttrNumber ) 8 , "usage_count" ,
95
118
INT2OID , -1 , 0 );
96
119
120
+ if (expected_tupledesc -> natts == NUM_BUFFERCACHE_PAGES_ELEM )
121
+ TupleDescInitEntry (tupledesc , (AttrNumber ) 9 , "pinning_backends" ,
122
+ INT4OID , -1 , 0 );
123
+
97
124
fctx -> tupdesc = BlessTupleDesc (tupledesc );
98
125
99
126
/* Allocate NBuffers worth of BufferCachePagesRec records. */
@@ -131,6 +158,7 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
131
158
fctx -> record [i ].forknum = bufHdr -> tag .forkNum ;
132
159
fctx -> record [i ].blocknum = bufHdr -> tag .blockNum ;
133
160
fctx -> record [i ].usagecount = bufHdr -> usage_count ;
161
+ fctx -> record [i ].pinning_backends = bufHdr -> refcount ;
134
162
135
163
if (bufHdr -> flags & BM_DIRTY )
136
164
fctx -> record [i ].isdirty = true;
@@ -185,6 +213,8 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
185
213
nulls [5 ] = true;
186
214
nulls [6 ] = true;
187
215
nulls [7 ] = true;
216
+ /* unused for v1.0 callers, but the array is always long enough */
217
+ nulls [8 ] = true;
188
218
}
189
219
else
190
220
{
@@ -202,6 +232,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS)
202
232
nulls [6 ] = false;
203
233
values [7 ] = Int16GetDatum (fctx -> record [i ].usagecount );
204
234
nulls [7 ] = false;
235
+ /* unused for v1.0 callers, but the array is always long enough */
236
+ values [8 ] = Int32GetDatum (fctx -> record [i ].pinning_backends );
237
+ nulls [8 ] = false;
205
238
}
206
239
207
240
/* Build and return the tuple. */
0 commit comments