diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile index c5297d98d48..065d3d690a9 100644 --- a/contrib/pg_buffercache/Makefile +++ b/contrib/pg_buffercache/Makefile @@ -4,7 +4,7 @@ MODULE_big = pg_buffercache OBJS = pg_buffercache_pages.o $(WIN32RES) EXTENSION = pg_buffercache -DATA = pg_buffercache--1.0.sql pg_buffercache--unpackaged--1.0.sql +DATA = pg_buffercache--1.1.sql pg_buffercache--1.0--1.1.sql pg_buffercache--unpackaged--1.0.sql PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time" ifdef USE_PGXS diff --git a/contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql b/contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql new file mode 100644 index 00000000000..54d02f58c01 --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql @@ -0,0 +1,11 @@ +/* contrib/pg_buffercache/pg_buffercache--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.1'" to load this file. \quit + +-- Upgrade view to 1.1. format +CREATE OR REPLACE VIEW pg_buffercache AS + SELECT P.* FROM pg_buffercache_pages() AS P + (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid, + relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2, + pinning_backends int4); diff --git a/contrib/pg_buffercache/pg_buffercache--1.0.sql b/contrib/pg_buffercache/pg_buffercache--1.1.sql similarity index 88% rename from contrib/pg_buffercache/pg_buffercache--1.0.sql rename to contrib/pg_buffercache/pg_buffercache--1.1.sql index 4ca4c44256c..f3b6482fa62 100644 --- a/contrib/pg_buffercache/pg_buffercache--1.0.sql +++ b/contrib/pg_buffercache/pg_buffercache--1.1.sql @@ -1,4 +1,4 @@ -/* contrib/pg_buffercache/pg_buffercache--1.0.sql */ +/* contrib/pg_buffercache/pg_buffercache--1.1.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_buffercache" to load this file. \quit @@ -13,7 +13,8 @@ LANGUAGE C; CREATE VIEW pg_buffercache AS SELECT P.* FROM pg_buffercache_pages() AS P (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid, - relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2); + relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2, + pinning_backends int4); -- Don't want these to be available to public. REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control index 709513c334e..5494e2fae52 100644 --- a/contrib/pg_buffercache/pg_buffercache.control +++ b/contrib/pg_buffercache/pg_buffercache.control @@ -1,5 +1,5 @@ # pg_buffercache extension comment = 'examine the shared buffer cache' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/pg_buffercache' relocatable = true diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index b205683bffc..d3b1ba3245d 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -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. */ diff --git a/doc/src/sgml/pgbuffercache.sgml b/doc/src/sgml/pgbuffercache.sgml index 4eb02c06239..f379be225f0 100644 --- a/doc/src/sgml/pgbuffercache.sgml +++ b/doc/src/sgml/pgbuffercache.sgml @@ -106,6 +106,13 @@ Clock-sweep access count + + pinning_backends + integer + + Number of backends pinning this buffer + +