LCOV - differential code coverage report
Current view: top level - contrib/pg_buffercache - pg_buffercache_pages.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DUB DCB
Current: 7a15cff1f11193467898da1c1fabf06fd2caee04 vs 84a3778c79c2d28b4dc281d03ef2ab019b16483b Lines: 94.7 % 342 324 6 12 93 231 1 30
Current Date: 2025-12-15 18:36:29 -0500 Functions: 96.0 % 25 24 1 10 14 1
Baseline: lcov-20251216-010103-baseline Branches: 61.9 % 168 104 20 44 38 66
Baseline Date: 2025-12-15 13:30:48 -0800 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 4 4 4
(7,30] days: 93.5 % 92 86 6 86
(30,360] days: 94.4 % 107 101 6 3 98
(360..) days: 95.7 % 139 133 6 133
Function coverage date bins:
(7,30] days: 90.0 % 10 9 1 9
(30,360] days: 100.0 % 7 7 7
(360..) days: 100.0 % 8 8 1 7
Branch coverage date bins:
(7,30] days: 65.5 % 58 38 20 38
(30,360] days: 50.0 % 54 27 27 27
(360..) days: 69.6 % 56 39 17 39

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * pg_buffercache_pages.c
                                  4                 :                :  *    display some contents of the buffer cache
                                  5                 :                :  *
                                  6                 :                :  *    contrib/pg_buffercache/pg_buffercache_pages.c
                                  7                 :                :  *-------------------------------------------------------------------------
                                  8                 :                :  */
                                  9                 :                : #include "postgres.h"
                                 10                 :                : 
                                 11                 :                : #include "access/htup_details.h"
                                 12                 :                : #include "access/relation.h"
                                 13                 :                : #include "catalog/pg_type.h"
                                 14                 :                : #include "funcapi.h"
                                 15                 :                : #include "port/pg_numa.h"
                                 16                 :                : #include "storage/buf_internals.h"
                                 17                 :                : #include "storage/bufmgr.h"
                                 18                 :                : #include "utils/rel.h"
                                 19                 :                : 
                                 20                 :                : 
                                 21                 :                : #define NUM_BUFFERCACHE_PAGES_MIN_ELEM  8
                                 22                 :                : #define NUM_BUFFERCACHE_PAGES_ELEM  9
                                 23                 :                : #define NUM_BUFFERCACHE_SUMMARY_ELEM 5
                                 24                 :                : #define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
                                 25                 :                : #define NUM_BUFFERCACHE_EVICT_ELEM 2
                                 26                 :                : #define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
                                 27                 :                : #define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
                                 28                 :                : #define NUM_BUFFERCACHE_MARK_DIRTY_ELEM 2
                                 29                 :                : #define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM 3
                                 30                 :                : #define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM 3
                                 31                 :                : 
                                 32                 :                : #define NUM_BUFFERCACHE_OS_PAGES_ELEM   3
                                 33                 :                : 
  265 tgl@sss.pgh.pa.us          34                 :CBC           2 : PG_MODULE_MAGIC_EXT(
                                 35                 :                :                     .name = "pg_buffercache",
                                 36                 :                :                     .version = PG_VERSION
                                 37                 :                : );
                                 38                 :                : 
                                 39                 :                : /*
                                 40                 :                :  * Record structure holding the to be exposed cache data.
                                 41                 :                :  */
                                 42                 :                : typedef struct
                                 43                 :                : {
                                 44                 :                :     uint32      bufferid;
                                 45                 :                :     RelFileNumber relfilenumber;
                                 46                 :                :     Oid         reltablespace;
                                 47                 :                :     Oid         reldatabase;
                                 48                 :                :     ForkNumber  forknum;
                                 49                 :                :     BlockNumber blocknum;
                                 50                 :                :     bool        isvalid;
                                 51                 :                :     bool        isdirty;
                                 52                 :                :     uint16      usagecount;
                                 53                 :                : 
                                 54                 :                :     /*
                                 55                 :                :      * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
                                 56                 :                :      * being pinned by too many backends and each backend will only pin once
                                 57                 :                :      * because of bufmgr.c's PrivateRefCount infrastructure.
                                 58                 :                :      */
                                 59                 :                :     int32       pinning_backends;
                                 60                 :                : } BufferCachePagesRec;
                                 61                 :                : 
                                 62                 :                : 
                                 63                 :                : /*
                                 64                 :                :  * Function context for data persisting over repeated calls.
                                 65                 :                :  */
                                 66                 :                : typedef struct
                                 67                 :                : {
                                 68                 :                :     TupleDesc   tupdesc;
                                 69                 :                :     BufferCachePagesRec *record;
                                 70                 :                : } BufferCachePagesContext;
                                 71                 :                : 
                                 72                 :                : /*
                                 73                 :                :  * Record structure holding the to be exposed cache data for OS pages.  This
                                 74                 :                :  * structure is used by pg_buffercache_os_pages(), where NUMA information may
                                 75                 :                :  * or may not be included.
                                 76                 :                :  */
                                 77                 :                : typedef struct
                                 78                 :                : {
                                 79                 :                :     uint32      bufferid;
                                 80                 :                :     int64       page_num;
                                 81                 :                :     int32       numa_node;
                                 82                 :                : } BufferCacheOsPagesRec;
                                 83                 :                : 
                                 84                 :                : /*
                                 85                 :                :  * Function context for data persisting over repeated calls.
                                 86                 :                :  */
                                 87                 :                : typedef struct
                                 88                 :                : {
                                 89                 :                :     TupleDesc   tupdesc;
                                 90                 :                :     bool        include_numa;
                                 91                 :                :     BufferCacheOsPagesRec *record;
                                 92                 :                : } BufferCacheOsPagesContext;
                                 93                 :                : 
                                 94                 :                : 
                                 95                 :                : /*
                                 96                 :                :  * Function returning data from the shared buffer cache - buffer number,
                                 97                 :                :  * relation node/tablespace/database/blocknum and dirty indicator.
                                 98                 :                :  */
 7584 neilc@samurai.com          99                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_pages);
   22 michael@paquier.xyz       100                 :GNC           3 : PG_FUNCTION_INFO_V1(pg_buffercache_os_pages);
  253 tomas.vondra@postgre      101                 :CBC           1 : PG_FUNCTION_INFO_V1(pg_buffercache_numa_pages);
 1160 andres@anarazel.de        102                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_summary);
  984 tgl@sss.pgh.pa.us         103                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_usage_counts);
  618 tmunro@postgresql.or      104                 :              3 : PG_FUNCTION_INFO_V1(pg_buffercache_evict);
  252 andres@anarazel.de        105                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_evict_relation);
                                106                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_evict_all);
   18 michael@paquier.xyz       107                 :GNC           2 : PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty);
                                108                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty_relation);
                                109                 :              2 : PG_FUNCTION_INFO_V1(pg_buffercache_mark_dirty_all);
                                110                 :                : 
                                111                 :                : 
                                112                 :                : /* Only need to touch memory once per backend process lifetime */
                                113                 :                : static bool firstNumaTouch = true;
                                114                 :                : 
                                115                 :                : 
                                116                 :                : Datum
 1175 rhaas@postgresql.org      117                 :CBC       32770 : pg_buffercache_pages(PG_FUNCTION_ARGS)
                                118                 :                : {
                                119                 :                :     FuncCallContext *funcctx;
                                120                 :                :     Datum       result;
                                121                 :                :     MemoryContext oldcontext;
                                122                 :                :     BufferCachePagesContext *fctx;  /* User function context. */
                                123                 :                :     TupleDesc   tupledesc;
                                124                 :                :     TupleDesc   expected_tupledesc;
                                125                 :                :     HeapTuple   tuple;
                                126                 :                : 
 7584 neilc@samurai.com         127         [ +  + ]:          32770 :     if (SRF_IS_FIRSTCALL())
                                128                 :                :     {
                                129                 :                :         int         i;
                                130                 :                : 
                                131                 :              2 :         funcctx = SRF_FIRSTCALL_INIT();
                                132                 :                : 
                                133                 :                :         /* Switch context when allocating stuff to be used in later calls */
                                134                 :              2 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
                                135                 :                : 
                                136                 :                :         /* Create a user function context for cross-call persistence */
    5 michael@paquier.xyz       137                 :GNC           2 :         fctx = palloc_object(BufferCachePagesContext);
                                138                 :                : 
                                139                 :                :         /*
                                140                 :                :          * To smoothly support upgrades from version 1.0 of this extension
                                141                 :                :          * transparently handle the (non-)existence of the pinning_backends
                                142                 :                :          * column. We unfortunately have to get the result type for that... -
                                143                 :                :          * we can't use the result type determined by the function definition
                                144                 :                :          * without potentially crashing when somebody uses the old (or even
                                145                 :                :          * wrong) function definition though.
                                146                 :                :          */
 4134 andres@anarazel.de        147         [ -  + ]:CBC           2 :         if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
 4134 andres@anarazel.de        148         [ #  # ]:UBC           0 :             elog(ERROR, "return type must be a row type");
                                149                 :                : 
 4134 andres@anarazel.de        150         [ +  - ]:CBC           2 :         if (expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
                                151         [ -  + ]:              2 :             expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
 4134 andres@anarazel.de        152         [ #  # ]:UBC           0 :             elog(ERROR, "incorrect number of output arguments");
                                153                 :                : 
                                154                 :                :         /* Construct a tuple descriptor for the result rows. */
 2583 andres@anarazel.de        155                 :CBC           2 :         tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
 7584 neilc@samurai.com         156                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
                                157                 :                :                            INT4OID, -1, 0);
                                158                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
                                159                 :                :                            OIDOID, -1, 0);
                                160                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
                                161                 :                :                            OIDOID, -1, 0);
                                162                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
                                163                 :                :                            OIDOID, -1, 0);
 6333 heikki.linnakangas@i      164                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
                                165                 :                :                            INT2OID, -1, 0);
                                166                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
                                167                 :                :                            INT8OID, -1, 0);
                                168                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
                                169                 :                :                            BOOLOID, -1, 0);
                                170                 :              2 :         TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
                                171                 :                :                            INT2OID, -1, 0);
                                172                 :                : 
 4134 andres@anarazel.de        173         [ +  - ]:              2 :         if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
                                174                 :              2 :             TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
                                175                 :                :                                INT4OID, -1, 0);
                                176                 :                : 
 6995 tgl@sss.pgh.pa.us         177                 :              2 :         fctx->tupdesc = BlessTupleDesc(tupledesc);
                                178                 :                : 
                                179                 :                :         /* Allocate NBuffers worth of BufferCachePagesRec records. */
 3379 rhaas@postgresql.org      180                 :              2 :         fctx->record = (BufferCachePagesRec *)
                                181                 :              2 :             MemoryContextAllocHuge(CurrentMemoryContext,
                                182                 :                :                                    sizeof(BufferCachePagesRec) * NBuffers);
                                183                 :                : 
                                184                 :                :         /* Set max calls and remember the user function context. */
 6995 tgl@sss.pgh.pa.us         185                 :              2 :         funcctx->max_calls = NBuffers;
                                186                 :              2 :         funcctx->user_fctx = fctx;
                                187                 :                : 
                                188                 :                :         /* Return to original context when allocating transient memory */
 7584 neilc@samurai.com         189                 :              2 :         MemoryContextSwitchTo(oldcontext);
                                190                 :                : 
                                191                 :                :         /*
                                192                 :                :          * Scan through all the buffers, saving the relevant fields in the
                                193                 :                :          * fctx->record structure.
                                194                 :                :          *
                                195                 :                :          * We don't hold the partition locks, so we don't get a consistent
                                196                 :                :          * snapshot across all buffers, but we do grab the buffer header
                                197                 :                :          * locks, so the information of each buffer is self-consistent.
                                198                 :                :          */
 3974 andres@anarazel.de        199         [ +  + ]:          32770 :         for (i = 0; i < NBuffers; i++)
                                200                 :                :         {
                                201                 :                :             BufferDesc *bufHdr;
                                202                 :                :             uint32      buf_state;
                                203                 :                : 
  119 msawada@postgresql.o      204         [ -  + ]:          32768 :             CHECK_FOR_INTERRUPTS();
                                205                 :                : 
 3974 andres@anarazel.de        206                 :          32768 :             bufHdr = GetBufferDescriptor(i);
                                207                 :                :             /* Lock each buffer header before inspecting. */
 3537                           208                 :          32768 :             buf_state = LockBufHdr(bufHdr);
                                209                 :                : 
 7584 neilc@samurai.com         210                 :          32768 :             fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
 1210 rhaas@postgresql.org      211                 :          32768 :             fctx->record[i].relfilenumber = BufTagGetRelNumber(&bufHdr->tag);
                                212                 :          32768 :             fctx->record[i].reltablespace = bufHdr->tag.spcOid;
                                213                 :          32768 :             fctx->record[i].reldatabase = bufHdr->tag.dbOid;
                                214                 :          32768 :             fctx->record[i].forknum = BufTagGetForkNum(&bufHdr->tag);
 7584 neilc@samurai.com         215                 :          32768 :             fctx->record[i].blocknum = bufHdr->tag.blockNum;
 3537 andres@anarazel.de        216                 :          32768 :             fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
                                217                 :          32768 :             fctx->record[i].pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
                                218                 :                : 
                                219         [ +  + ]:          32768 :             if (buf_state & BM_DIRTY)
 7584 neilc@samurai.com         220                 :           1940 :                 fctx->record[i].isdirty = true;
                                221                 :                :             else
                                222                 :          30828 :                 fctx->record[i].isdirty = false;
                                223                 :                : 
                                224                 :                :             /* Note if the buffer is valid, and has storage created */
 3537 andres@anarazel.de        225   [ +  +  +  - ]:          32768 :             if ((buf_state & BM_VALID) && (buf_state & BM_TAG_VALID))
 7584 neilc@samurai.com         226                 :           4076 :                 fctx->record[i].isvalid = true;
                                227                 :                :             else
                                228                 :          28692 :                 fctx->record[i].isvalid = false;
                                229                 :                : 
   40 andres@anarazel.de        230                 :GNC       32768 :             UnlockBufHdr(bufHdr);
                                231                 :                :         }
                                232                 :                :     }
                                233                 :                : 
 7584 neilc@samurai.com         234                 :CBC       32770 :     funcctx = SRF_PERCALL_SETUP();
                                235                 :                : 
                                236                 :                :     /* Get the saved state */
                                237                 :          32770 :     fctx = funcctx->user_fctx;
                                238                 :                : 
                                239         [ +  + ]:          32770 :     if (funcctx->call_cntr < funcctx->max_calls)
                                240                 :                :     {
 7367 bruce@momjian.us          241                 :          32768 :         uint32      i = funcctx->call_cntr;
                                242                 :                :         Datum       values[NUM_BUFFERCACHE_PAGES_ELEM];
                                243                 :                :         bool        nulls[NUM_BUFFERCACHE_PAGES_ELEM];
                                244                 :                : 
 6995 tgl@sss.pgh.pa.us         245                 :          32768 :         values[0] = Int32GetDatum(fctx->record[i].bufferid);
                                246                 :          32768 :         nulls[0] = false;
                                247                 :                : 
                                248                 :                :         /*
                                249                 :                :          * Set all fields except the bufferid to null if the buffer is unused
                                250                 :                :          * or not valid.
                                251                 :                :          */
 7584 neilc@samurai.com         252         [ +  + ]:          32768 :         if (fctx->record[i].blocknum == InvalidBlockNumber ||
 7367 bruce@momjian.us          253         [ -  + ]:           4076 :             fctx->record[i].isvalid == false)
                                254                 :                :         {
 6995 tgl@sss.pgh.pa.us         255                 :          28692 :             nulls[1] = true;
                                256                 :          28692 :             nulls[2] = true;
                                257                 :          28692 :             nulls[3] = true;
                                258                 :          28692 :             nulls[4] = true;
                                259                 :          28692 :             nulls[5] = true;
 6828 bruce@momjian.us          260                 :          28692 :             nulls[6] = true;
 6333 heikki.linnakangas@i      261                 :          28692 :             nulls[7] = true;
                                262                 :                :             /* unused for v1.0 callers, but the array is always long enough */
 4134 andres@anarazel.de        263                 :          28692 :             nulls[8] = true;
                                264                 :                :         }
                                265                 :                :         else
                                266                 :                :         {
 1175 rhaas@postgresql.org      267                 :           4076 :             values[1] = ObjectIdGetDatum(fctx->record[i].relfilenumber);
 6995 tgl@sss.pgh.pa.us         268                 :           4076 :             nulls[1] = false;
                                269                 :           4076 :             values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
                                270                 :           4076 :             nulls[2] = false;
                                271                 :           4076 :             values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
                                272                 :           4076 :             nulls[3] = false;
   28 michael@paquier.xyz       273                 :GNC        4076 :             values[4] = Int16GetDatum(fctx->record[i].forknum);
 6995 tgl@sss.pgh.pa.us         274                 :CBC        4076 :             nulls[4] = false;
 6333 heikki.linnakangas@i      275                 :           4076 :             values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
 6995 tgl@sss.pgh.pa.us         276                 :           4076 :             nulls[5] = false;
 6333 heikki.linnakangas@i      277                 :           4076 :             values[6] = BoolGetDatum(fctx->record[i].isdirty);
 6828 bruce@momjian.us          278                 :           4076 :             nulls[6] = false;
 6333 heikki.linnakangas@i      279                 :           4076 :             values[7] = Int16GetDatum(fctx->record[i].usagecount);
                                280                 :           4076 :             nulls[7] = false;
                                281                 :                :             /* unused for v1.0 callers, but the array is always long enough */
 4134 andres@anarazel.de        282                 :           4076 :             values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
                                283                 :           4076 :             nulls[8] = false;
                                284                 :                :         }
                                285                 :                : 
                                286                 :                :         /* Build and return the tuple. */
 6995 tgl@sss.pgh.pa.us         287                 :          32768 :         tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
 7584 neilc@samurai.com         288                 :          32768 :         result = HeapTupleGetDatum(tuple);
                                289                 :                : 
                                290                 :          32768 :         SRF_RETURN_NEXT(funcctx, result);
                                291                 :                :     }
                                292                 :                :     else
                                293                 :              2 :         SRF_RETURN_DONE(funcctx);
                                294                 :                : }
                                295                 :                : 
                                296                 :                : /*
                                297                 :                :  * Inquire about OS pages mappings for shared buffers, with NUMA information,
                                298                 :                :  * optionally.
                                299                 :                :  *
                                300                 :                :  * When "include_numa" is false, this routines ignores everything related
                                301                 :                :  * to NUMA (returned as NULL values), returning mapping information between
                                302                 :                :  * shared buffers and OS pages.
                                303                 :                :  *
                                304                 :                :  * When "include_numa" is true, NUMA is initialized and numa_node values
                                305                 :                :  * are generated.  In order to get reliable results we also need to touch
                                306                 :                :  * memory pages, so that the inquiry about NUMA memory node does not return
                                307                 :                :  * -2, indicating unmapped/unallocated pages.
                                308                 :                :  *
                                309                 :                :  * Buffers may be smaller or larger than OS memory pages. For each buffer we
                                310                 :                :  * return one entry for each memory page used by the buffer (if the buffer is
                                311                 :                :  * smaller, it only uses a part of one memory page).
                                312                 :                :  *
                                313                 :                :  * We expect both sizes (for buffers and memory pages) to be a power-of-2, so
                                314                 :                :  * one is always a multiple of the other.
                                315                 :                :  *
                                316                 :                :  */
                                317                 :                : static Datum
   22 michael@paquier.xyz       318                 :GNC      131076 : pg_buffercache_os_pages_internal(FunctionCallInfo fcinfo, bool include_numa)
                                319                 :                : {
                                320                 :                :     FuncCallContext *funcctx;
                                321                 :                :     MemoryContext oldcontext;
                                322                 :                :     BufferCacheOsPagesContext *fctx;    /* User function context. */
                                323                 :                :     TupleDesc   tupledesc;
                                324                 :                :     TupleDesc   expected_tupledesc;
                                325                 :                :     HeapTuple   tuple;
                                326                 :                :     Datum       result;
                                327                 :                : 
  253 tomas.vondra@postgre      328         [ +  + ]:CBC      131076 :     if (SRF_IS_FIRSTCALL())
                                329                 :                :     {
                                330                 :                :         int         i,
                                331                 :                :                     idx;
                                332                 :                :         Size        os_page_size;
                                333                 :                :         int         pages_per_buffer;
   22 michael@paquier.xyz       334                 :GNC           4 :         int        *os_page_status = NULL;
                                335                 :              4 :         uint64      os_page_count = 0;
                                336                 :                :         int         max_entries;
                                337                 :                :         char       *startptr,
                                338                 :                :                    *endptr;
                                339                 :                : 
                                340                 :                :         /* If NUMA information is requested, initialize NUMA support. */
                                341   [ +  +  -  + ]:              4 :         if (include_numa && pg_numa_init() == -1)
  253 tomas.vondra@postgre      342         [ #  # ]:UBC           0 :             elog(ERROR, "libnuma initialization failed or NUMA is not supported on this platform");
                                343                 :                : 
                                344                 :                :         /*
                                345                 :                :          * The database block size and OS memory page size are unlikely to be
                                346                 :                :          * the same. The block size is 1-32KB, the memory page size depends on
                                347                 :                :          * platform. On x86 it's usually 4KB, on ARM it's 4KB or 64KB, but
                                348                 :                :          * there are also features like THP etc. Moreover, we don't quite know
                                349                 :                :          * how the pages and buffers "align" in memory - the buffers may be
                                350                 :                :          * shifted in some way, using more memory pages than necessary.
                                351                 :                :          *
                                352                 :                :          * So we need to be careful about mapping buffers to memory pages. We
                                353                 :                :          * calculate the maximum number of pages a buffer might use, so that
                                354                 :                :          * we allocate enough space for the entries. And then we count the
                                355                 :                :          * actual number of entries as we scan the buffers.
                                356                 :                :          *
                                357                 :                :          * This information is needed before calling move_pages() for NUMA
                                358                 :                :          * node id inquiry.
                                359                 :                :          */
  251 tomas.vondra@postgre      360                 :CBC           4 :         os_page_size = pg_get_shmem_pagesize();
                                361                 :                : 
                                362                 :                :         /*
                                363                 :                :          * The pages and block size is expected to be 2^k, so one divides the
                                364                 :                :          * other (we don't know in which direction). This does not say
                                365                 :                :          * anything about relative alignment of pages/buffers.
                                366                 :                :          */
  253                           367   [ +  -  -  + ]:              4 :         Assert((os_page_size % BLCKSZ == 0) || (BLCKSZ % os_page_size == 0));
                                368                 :                : 
   22 michael@paquier.xyz       369         [ +  + ]:GNC           4 :         if (include_numa)
                                370                 :                :         {
                                371                 :              2 :             void      **os_page_ptrs = NULL;
                                372                 :                : 
                                373                 :                :             /*
                                374                 :                :              * How many addresses we are going to query?  Simply get the page
                                375                 :                :              * for the first buffer, and first page after the last buffer, and
                                376                 :                :              * count the pages from that.
                                377                 :                :              */
                                378                 :              2 :             startptr = (char *) TYPEALIGN_DOWN(os_page_size,
                                379                 :                :                                                BufferGetBlock(1));
                                380                 :              2 :             endptr = (char *) TYPEALIGN(os_page_size,
                                381                 :                :                                         (char *) BufferGetBlock(NBuffers) + BLCKSZ);
                                382                 :              2 :             os_page_count = (endptr - startptr) / os_page_size;
                                383                 :                : 
                                384                 :                :             /* Used to determine the NUMA node for all OS pages at once */
    5                           385                 :              2 :             os_page_ptrs = palloc0_array(void *, os_page_count);
                                386                 :              2 :             os_page_status = palloc_array(int, os_page_count);
                                387                 :                : 
                                388                 :                :             /*
                                389                 :                :              * Fill pointers for all the memory pages.  This loop stores and
                                390                 :                :              * touches (if needed) addresses into os_page_ptrs[] as input to
                                391                 :                :              * one big move_pages(2) inquiry system call, as done in
                                392                 :                :              * pg_numa_query_pages().
                                393                 :                :              */
   22                           394                 :              2 :             idx = 0;
                                395         [ +  + ]:          65538 :             for (char *ptr = startptr; ptr < endptr; ptr += os_page_size)
                                396                 :                :             {
                                397                 :          65536 :                 os_page_ptrs[idx++] = ptr;
                                398                 :                : 
                                399                 :                :                 /* Only need to touch memory once per backend process lifetime */
                                400         [ +  + ]:          65536 :                 if (firstNumaTouch)
                                401                 :          32768 :                     pg_numa_touch_mem_if_required(ptr);
                                402                 :                :             }
                                403                 :                : 
                                404         [ -  + ]:              2 :             Assert(idx == os_page_count);
                                405                 :                : 
                                406         [ -  + ]:              2 :             elog(DEBUG1, "NUMA: NBuffers=%d os_page_count=" UINT64_FORMAT " "
                                407                 :                :                  "os_page_size=%zu", NBuffers, os_page_count, os_page_size);
                                408                 :                : 
                                409                 :                :             /*
                                410                 :                :              * If we ever get 0xff back from kernel inquiry, then we probably
                                411                 :                :              * have bug in our buffers to OS page mapping code here.
                                412                 :                :              */
                                413                 :              2 :             memset(os_page_status, 0xff, sizeof(int) * os_page_count);
                                414                 :                : 
                                415                 :                :             /* Query NUMA status for all the pointers */
                                416         [ -  + ]:              2 :             if (pg_numa_query_pages(0, os_page_count, os_page_ptrs, os_page_status) == -1)
   22 michael@paquier.xyz       417         [ #  # ]:UNC           0 :                 elog(ERROR, "failed NUMA pages inquiry: %m");
                                418                 :                :         }
                                419                 :                : 
                                420                 :                :         /* Initialize the multi-call context, load entries about buffers */
                                421                 :                : 
  253 tomas.vondra@postgre      422                 :CBC           4 :         funcctx = SRF_FIRSTCALL_INIT();
                                423                 :                : 
                                424                 :                :         /* Switch context when allocating stuff to be used in later calls */
                                425                 :              4 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
                                426                 :                : 
                                427                 :                :         /* Create a user function context for cross-call persistence */
    5 michael@paquier.xyz       428                 :GNC           4 :         fctx = palloc_object(BufferCacheOsPagesContext);
                                429                 :                : 
  253 tomas.vondra@postgre      430         [ -  + ]:CBC           4 :         if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
  253 tomas.vondra@postgre      431         [ #  # ]:UBC           0 :             elog(ERROR, "return type must be a row type");
                                432                 :                : 
   22 michael@paquier.xyz       433         [ -  + ]:GNC           4 :         if (expected_tupledesc->natts != NUM_BUFFERCACHE_OS_PAGES_ELEM)
  253 tomas.vondra@postgre      434         [ #  # ]:UBC           0 :             elog(ERROR, "incorrect number of output arguments");
                                435                 :                : 
                                436                 :                :         /* Construct a tuple descriptor for the result rows. */
  253 tomas.vondra@postgre      437                 :CBC           4 :         tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
                                438                 :              4 :         TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
                                439                 :                :                            INT4OID, -1, 0);
                                440                 :              4 :         TupleDescInitEntry(tupledesc, (AttrNumber) 2, "os_page_num",
                                441                 :                :                            INT8OID, -1, 0);
                                442                 :              4 :         TupleDescInitEntry(tupledesc, (AttrNumber) 3, "numa_node",
                                443                 :                :                            INT4OID, -1, 0);
                                444                 :                : 
                                445                 :              4 :         fctx->tupdesc = BlessTupleDesc(tupledesc);
   22 michael@paquier.xyz       446                 :GNC           4 :         fctx->include_numa = include_numa;
                                447                 :                : 
                                448                 :                :         /*
                                449                 :                :          * Each buffer needs at least one entry, but it might be offset in
                                450                 :                :          * some way, and use one extra entry. So we allocate space for the
                                451                 :                :          * maximum number of entries we might need, and then count the exact
                                452                 :                :          * number as we're walking buffers. That way we can do it in one pass,
                                453                 :                :          * without reallocating memory.
                                454                 :                :          */
  253 tomas.vondra@postgre      455         [ +  - ]:CBC           4 :         pages_per_buffer = Max(1, BLCKSZ / os_page_size) + 1;
                                456                 :              4 :         max_entries = NBuffers * pages_per_buffer;
                                457                 :                : 
                                458                 :                :         /* Allocate entries for BufferCacheOsPagesRec records. */
   22 michael@paquier.xyz       459                 :GNC           4 :         fctx->record = (BufferCacheOsPagesRec *)
  253 tomas.vondra@postgre      460                 :CBC           4 :             MemoryContextAllocHuge(CurrentMemoryContext,
                                461                 :                :                                    sizeof(BufferCacheOsPagesRec) * max_entries);
                                462                 :                : 
                                463                 :                :         /* Return to original context when allocating transient memory */
                                464                 :              4 :         MemoryContextSwitchTo(oldcontext);
                                465                 :                : 
   22 michael@paquier.xyz       466   [ +  +  +  + ]:GNC           4 :         if (include_numa && firstNumaTouch)
  253 tomas.vondra@postgre      467         [ -  + ]:CBC           1 :             elog(DEBUG1, "NUMA: page-faulting the buffercache for proper NUMA readouts");
                                468                 :                : 
                                469                 :                :         /*
                                470                 :                :          * Scan through all the buffers, saving the relevant fields in the
                                471                 :                :          * fctx->record structure.
                                472                 :                :          *
                                473                 :                :          * We don't hold the partition locks, so we don't get a consistent
                                474                 :                :          * snapshot across all buffers, but we do grab the buffer header
                                475                 :                :          * locks, so the information of each buffer is self-consistent.
                                476                 :                :          */
                                477                 :              4 :         startptr = (char *) TYPEALIGN_DOWN(os_page_size, (char *) BufferGetBlock(1));
                                478                 :              4 :         idx = 0;
                                479         [ +  + ]:          65540 :         for (i = 0; i < NBuffers; i++)
                                480                 :                :         {
                                481                 :          65536 :             char       *buffptr = (char *) BufferGetBlock(i + 1);
                                482                 :                :             BufferDesc *bufHdr;
                                483                 :                :             uint32      bufferid;
                                484                 :                :             int32       page_num;
                                485                 :                :             char       *startptr_buff,
                                486                 :                :                        *endptr_buff;
                                487                 :                : 
                                488         [ -  + ]:          65536 :             CHECK_FOR_INTERRUPTS();
                                489                 :                : 
                                490                 :          65536 :             bufHdr = GetBufferDescriptor(i);
                                491                 :                : 
                                492                 :                :             /* Lock each buffer header before inspecting. */
   40 andres@anarazel.de        493                 :GNC       65536 :             LockBufHdr(bufHdr);
  253 tomas.vondra@postgre      494                 :CBC       65536 :             bufferid = BufferDescriptorGetBuffer(bufHdr);
   40 andres@anarazel.de        495                 :GNC       65536 :             UnlockBufHdr(bufHdr);
                                496                 :                : 
                                497                 :                :             /* start of the first page of this buffer */
  253 tomas.vondra@postgre      498                 :CBC       65536 :             startptr_buff = (char *) TYPEALIGN_DOWN(os_page_size, buffptr);
                                499                 :                : 
                                500                 :                :             /* end of the buffer (no need to align to memory page) */
                                501                 :          65536 :             endptr_buff = buffptr + BLCKSZ;
                                502                 :                : 
                                503         [ -  + ]:          65536 :             Assert(startptr_buff < endptr_buff);
                                504                 :                : 
                                505                 :                :             /* calculate ID of the first page for this buffer */
                                506                 :          65536 :             page_num = (startptr_buff - startptr) / os_page_size;
                                507                 :                : 
                                508                 :                :             /* Add an entry for each OS page overlapping with this buffer. */
                                509         [ +  + ]:         196608 :             for (char *ptr = startptr_buff; ptr < endptr_buff; ptr += os_page_size)
                                510                 :                :             {
                                511                 :         131072 :                 fctx->record[idx].bufferid = bufferid;
                                512                 :         131072 :                 fctx->record[idx].page_num = page_num;
   22 michael@paquier.xyz       513         [ +  + ]:GNC      131072 :                 fctx->record[idx].numa_node = include_numa ? os_page_status[page_num] : -1;
                                514                 :                : 
                                515                 :                :                 /* advance to the next entry/page */
  253 tomas.vondra@postgre      516                 :CBC      131072 :                 ++idx;
                                517                 :         131072 :                 ++page_num;
                                518                 :                :             }
                                519                 :                :         }
                                520                 :                : 
   22 michael@paquier.xyz       521         [ -  + ]:GNC           4 :         Assert(idx <= max_entries);
                                522                 :                : 
                                523         [ +  + ]:              4 :         if (include_numa)
                                524         [ -  + ]:              2 :             Assert(idx >= os_page_count);
                                525                 :                : 
                                526                 :                :         /* Set max calls and remember the user function context. */
  253 tomas.vondra@postgre      527                 :CBC           4 :         funcctx->max_calls = idx;
                                528                 :              4 :         funcctx->user_fctx = fctx;
                                529                 :                : 
                                530                 :                :         /* Remember this backend touched the pages (only relevant for NUMA) */
   22 michael@paquier.xyz       531         [ +  + ]:GNC           4 :         if (include_numa)
                                532                 :              2 :             firstNumaTouch = false;
                                533                 :                :     }
                                534                 :                : 
  253 tomas.vondra@postgre      535                 :CBC      131076 :     funcctx = SRF_PERCALL_SETUP();
                                536                 :                : 
                                537                 :                :     /* Get the saved state */
                                538                 :         131076 :     fctx = funcctx->user_fctx;
                                539                 :                : 
                                540         [ +  + ]:         131076 :     if (funcctx->call_cntr < funcctx->max_calls)
                                541                 :                :     {
                                542                 :         131072 :         uint32      i = funcctx->call_cntr;
                                543                 :                :         Datum       values[NUM_BUFFERCACHE_OS_PAGES_ELEM];
                                544                 :                :         bool        nulls[NUM_BUFFERCACHE_OS_PAGES_ELEM];
                                545                 :                : 
                                546                 :         131072 :         values[0] = Int32GetDatum(fctx->record[i].bufferid);
                                547                 :         131072 :         nulls[0] = false;
                                548                 :                : 
  252                           549                 :         131072 :         values[1] = Int64GetDatum(fctx->record[i].page_num);
  253                           550                 :         131072 :         nulls[1] = false;
                                551                 :                : 
   22 michael@paquier.xyz       552         [ +  + ]:GNC      131072 :         if (fctx->include_numa)
                                553                 :                :         {
                                554                 :          65536 :             values[2] = Int32GetDatum(fctx->record[i].numa_node);
                                555                 :          65536 :             nulls[2] = false;
                                556                 :                :         }
                                557                 :                :         else
                                558                 :                :         {
                                559                 :          65536 :             values[2] = (Datum) 0;
                                560                 :          65536 :             nulls[2] = true;
                                561                 :                :         }
                                562                 :                : 
                                563                 :                :         /* Build and return the tuple. */
  253 tomas.vondra@postgre      564                 :CBC      131072 :         tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
                                565                 :         131072 :         result = HeapTupleGetDatum(tuple);
                                566                 :                : 
                                567                 :         131072 :         SRF_RETURN_NEXT(funcctx, result);
                                568                 :                :     }
                                569                 :                :     else
                                570                 :              4 :         SRF_RETURN_DONE(funcctx);
                                571                 :                : }
                                572                 :                : 
                                573                 :                : /*
                                574                 :                :  * pg_buffercache_os_pages
                                575                 :                :  *
                                576                 :                :  * Retrieve information about OS pages, with or without NUMA information.
                                577                 :                :  */
                                578                 :                : Datum
   22 michael@paquier.xyz       579                 :GNC      131076 : pg_buffercache_os_pages(PG_FUNCTION_ARGS)
                                580                 :                : {
                                581                 :                :     bool        include_numa;
                                582                 :                : 
                                583                 :                :     /* Get the boolean parameter that controls the NUMA behavior. */
                                584                 :         131076 :     include_numa = PG_GETARG_BOOL(0);
                                585                 :                : 
                                586                 :         131076 :     return pg_buffercache_os_pages_internal(fcinfo, include_numa);
                                587                 :                : }
                                588                 :                : 
                                589                 :                : /* Backward-compatible wrapper for v1.6. */
                                590                 :                : Datum
   22 michael@paquier.xyz       591                 :UNC           0 : pg_buffercache_numa_pages(PG_FUNCTION_ARGS)
                                592                 :                : {
                                593                 :                :     /* Call internal function with include_numa=true */
                                594                 :              0 :     return pg_buffercache_os_pages_internal(fcinfo, true);
                                595                 :                : }
                                596                 :                : 
                                597                 :                : Datum
 1160 andres@anarazel.de        598                 :CBC           2 : pg_buffercache_summary(PG_FUNCTION_ARGS)
                                599                 :                : {
                                600                 :                :     Datum       result;
                                601                 :                :     TupleDesc   tupledesc;
                                602                 :                :     HeapTuple   tuple;
                                603                 :                :     Datum       values[NUM_BUFFERCACHE_SUMMARY_ELEM];
                                604                 :                :     bool        nulls[NUM_BUFFERCACHE_SUMMARY_ELEM];
                                605                 :                : 
                                606                 :              2 :     int32       buffers_used = 0;
                                607                 :              2 :     int32       buffers_unused = 0;
                                608                 :              2 :     int32       buffers_dirty = 0;
                                609                 :              2 :     int32       buffers_pinned = 0;
                                610                 :              2 :     int64       usagecount_total = 0;
                                611                 :                : 
                                612         [ -  + ]:              2 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
 1160 andres@anarazel.de        613         [ #  # ]:UBC           0 :         elog(ERROR, "return type must be a row type");
                                614                 :                : 
 1160 andres@anarazel.de        615         [ +  + ]:CBC       32770 :     for (int i = 0; i < NBuffers; i++)
                                616                 :                :     {
                                617                 :                :         BufferDesc *bufHdr;
                                618                 :                :         uint32      buf_state;
                                619                 :                : 
  119 msawada@postgresql.o      620         [ -  + ]:          32768 :         CHECK_FOR_INTERRUPTS();
                                621                 :                : 
                                622                 :                :         /*
                                623                 :                :          * This function summarizes the state of all headers. Locking the
                                624                 :                :          * buffer headers wouldn't provide an improved result as the state of
                                625                 :                :          * the buffer can still change after we release the lock and it'd
                                626                 :                :          * noticeably increase the cost of the function.
                                627                 :                :          */
 1160 andres@anarazel.de        628                 :          32768 :         bufHdr = GetBufferDescriptor(i);
                                629                 :          32768 :         buf_state = pg_atomic_read_u32(&bufHdr->state);
                                630                 :                : 
                                631         [ +  + ]:          32768 :         if (buf_state & BM_VALID)
                                632                 :                :         {
                                633                 :           4076 :             buffers_used++;
                                634                 :           4076 :             usagecount_total += BUF_STATE_GET_USAGECOUNT(buf_state);
                                635                 :                : 
                                636         [ +  + ]:           4076 :             if (buf_state & BM_DIRTY)
                                637                 :           1940 :                 buffers_dirty++;
                                638                 :                :         }
                                639                 :                :         else
                                640                 :          28692 :             buffers_unused++;
                                641                 :                : 
                                642         [ -  + ]:          32768 :         if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
 1160 andres@anarazel.de        643                 :UBC           0 :             buffers_pinned++;
                                644                 :                :     }
                                645                 :                : 
 1160 andres@anarazel.de        646                 :CBC           2 :     memset(nulls, 0, sizeof(nulls));
                                647                 :              2 :     values[0] = Int32GetDatum(buffers_used);
                                648                 :              2 :     values[1] = Int32GetDatum(buffers_unused);
                                649                 :              2 :     values[2] = Int32GetDatum(buffers_dirty);
                                650                 :              2 :     values[3] = Int32GetDatum(buffers_pinned);
                                651                 :                : 
                                652         [ +  - ]:              2 :     if (buffers_used != 0)
                                653                 :              2 :         values[4] = Float8GetDatum((double) usagecount_total / buffers_used);
                                654                 :                :     else
 1160 andres@anarazel.de        655                 :UBC           0 :         nulls[4] = true;
                                656                 :                : 
                                657                 :                :     /* Build and return the tuple. */
 1160 andres@anarazel.de        658                 :CBC           2 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                659                 :              2 :     result = HeapTupleGetDatum(tuple);
                                660                 :                : 
                                661                 :              2 :     PG_RETURN_DATUM(result);
                                662                 :                : }
                                663                 :                : 
                                664                 :                : Datum
  984 tgl@sss.pgh.pa.us         665                 :              2 : pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
                                666                 :                : {
                                667                 :              2 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
                                668                 :              2 :     int         usage_counts[BM_MAX_USAGE_COUNT + 1] = {0};
                                669                 :              2 :     int         dirty[BM_MAX_USAGE_COUNT + 1] = {0};
                                670                 :              2 :     int         pinned[BM_MAX_USAGE_COUNT + 1] = {0};
                                671                 :                :     Datum       values[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM];
                                672                 :              2 :     bool        nulls[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM] = {0};
                                673                 :                : 
                                674                 :              2 :     InitMaterializedSRF(fcinfo, 0);
                                675                 :                : 
                                676         [ +  + ]:          32770 :     for (int i = 0; i < NBuffers; i++)
                                677                 :                :     {
                                678                 :          32768 :         BufferDesc *bufHdr = GetBufferDescriptor(i);
                                679                 :          32768 :         uint32      buf_state = pg_atomic_read_u32(&bufHdr->state);
                                680                 :                :         int         usage_count;
                                681                 :                : 
  119 msawada@postgresql.o      682         [ -  + ]:          32768 :         CHECK_FOR_INTERRUPTS();
                                683                 :                : 
  984 tgl@sss.pgh.pa.us         684                 :          32768 :         usage_count = BUF_STATE_GET_USAGECOUNT(buf_state);
                                685                 :          32768 :         usage_counts[usage_count]++;
                                686                 :                : 
                                687         [ +  + ]:          32768 :         if (buf_state & BM_DIRTY)
                                688                 :           1940 :             dirty[usage_count]++;
                                689                 :                : 
                                690         [ -  + ]:          32768 :         if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
  984 tgl@sss.pgh.pa.us         691                 :UBC           0 :             pinned[usage_count]++;
                                692                 :                :     }
                                693                 :                : 
  984 tgl@sss.pgh.pa.us         694         [ +  + ]:CBC          14 :     for (int i = 0; i < BM_MAX_USAGE_COUNT + 1; i++)
                                695                 :                :     {
                                696                 :             12 :         values[0] = Int32GetDatum(i);
                                697                 :             12 :         values[1] = Int32GetDatum(usage_counts[i]);
                                698                 :             12 :         values[2] = Int32GetDatum(dirty[i]);
                                699                 :             12 :         values[3] = Int32GetDatum(pinned[i]);
                                700                 :                : 
                                701                 :             12 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
                                702                 :                :     }
                                703                 :                : 
                                704                 :              2 :     return (Datum) 0;
                                705                 :                : }
                                706                 :                : 
                                707                 :                : /*
                                708                 :                :  * Helper function to check if the user has superuser privileges.
                                709                 :                :  */
                                710                 :                : static void
  252 andres@anarazel.de        711                 :             20 : pg_buffercache_superuser_check(char *func_name)
                                712                 :                : {
                                713         [ +  + ]:             20 :     if (!superuser())
                                714         [ +  - ]:              6 :         ereport(ERROR,
                                715                 :                :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                716                 :                :                  errmsg("must be superuser to use %s()",
                                717                 :                :                         func_name)));
                                718                 :             14 : }
                                719                 :                : 
                                720                 :                : /*
                                721                 :                :  * Try to evict a shared buffer.
                                722                 :                :  */
                                723                 :                : Datum
  618 tmunro@postgresql.or      724                 :              5 : pg_buffercache_evict(PG_FUNCTION_ARGS)
                                725                 :                : {
                                726                 :                :     Datum       result;
                                727                 :                :     TupleDesc   tupledesc;
                                728                 :                :     HeapTuple   tuple;
                                729                 :                :     Datum       values[NUM_BUFFERCACHE_EVICT_ELEM];
  252 andres@anarazel.de        730                 :              5 :     bool        nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0};
                                731                 :                : 
  618 tmunro@postgresql.or      732                 :              5 :     Buffer      buf = PG_GETARG_INT32(0);
                                733                 :                :     bool        buffer_flushed;
                                734                 :                : 
  252 andres@anarazel.de        735         [ -  + ]:              5 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
  252 andres@anarazel.de        736         [ #  # ]:UBC           0 :         elog(ERROR, "return type must be a row type");
                                737                 :                : 
  252 andres@anarazel.de        738                 :CBC           5 :     pg_buffercache_superuser_check("pg_buffercache_evict");
                                739                 :                : 
  618 tmunro@postgresql.or      740   [ +  +  +  + ]:              4 :     if (buf < 1 || buf > NBuffers)
                                741         [ +  - ]:              3 :         elog(ERROR, "bad buffer ID: %d", buf);
                                742                 :                : 
  252 andres@anarazel.de        743                 :              1 :     values[0] = BoolGetDatum(EvictUnpinnedBuffer(buf, &buffer_flushed));
                                744                 :              1 :     values[1] = BoolGetDatum(buffer_flushed);
                                745                 :                : 
                                746                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                747                 :              1 :     result = HeapTupleGetDatum(tuple);
                                748                 :                : 
                                749                 :              1 :     PG_RETURN_DATUM(result);
                                750                 :                : }
                                751                 :                : 
                                752                 :                : /*
                                753                 :                :  * Try to evict specified relation.
                                754                 :                :  */
                                755                 :                : Datum
                                756                 :              3 : pg_buffercache_evict_relation(PG_FUNCTION_ARGS)
                                757                 :                : {
                                758                 :                :     Datum       result;
                                759                 :                :     TupleDesc   tupledesc;
                                760                 :                :     HeapTuple   tuple;
                                761                 :                :     Datum       values[NUM_BUFFERCACHE_EVICT_RELATION_ELEM];
                                762                 :              3 :     bool        nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0};
                                763                 :                : 
                                764                 :                :     Oid         relOid;
                                765                 :                :     Relation    rel;
                                766                 :                : 
                                767                 :              3 :     int32       buffers_evicted = 0;
                                768                 :              3 :     int32       buffers_flushed = 0;
                                769                 :              3 :     int32       buffers_skipped = 0;
                                770                 :                : 
                                771         [ -  + ]:              3 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
  252 andres@anarazel.de        772         [ #  # ]:UBC           0 :         elog(ERROR, "return type must be a row type");
                                773                 :                : 
  252 andres@anarazel.de        774                 :CBC           3 :     pg_buffercache_superuser_check("pg_buffercache_evict_relation");
                                775                 :                : 
                                776                 :              2 :     relOid = PG_GETARG_OID(0);
                                777                 :                : 
                                778                 :              2 :     rel = relation_open(relOid, AccessShareLock);
                                779                 :                : 
                                780         [ +  + ]:              2 :     if (RelationUsesLocalBuffers(rel))
                                781         [ +  - ]:              1 :         ereport(ERROR,
                                782                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                783                 :                :                  errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
                                784                 :                :                         "pg_buffercache_evict_relation")));
                                785                 :                : 
                                786                 :              1 :     EvictRelUnpinnedBuffers(rel, &buffers_evicted, &buffers_flushed,
                                787                 :                :                             &buffers_skipped);
                                788                 :                : 
                                789                 :              1 :     relation_close(rel, AccessShareLock);
                                790                 :                : 
                                791                 :              1 :     values[0] = Int32GetDatum(buffers_evicted);
                                792                 :              1 :     values[1] = Int32GetDatum(buffers_flushed);
                                793                 :              1 :     values[2] = Int32GetDatum(buffers_skipped);
                                794                 :                : 
                                795                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                796                 :              1 :     result = HeapTupleGetDatum(tuple);
                                797                 :                : 
                                798                 :              1 :     PG_RETURN_DATUM(result);
                                799                 :                : }
                                800                 :                : 
                                801                 :                : 
                                802                 :                : /*
                                803                 :                :  * Try to evict all shared buffers.
                                804                 :                :  */
                                805                 :                : Datum
                                806                 :              2 : pg_buffercache_evict_all(PG_FUNCTION_ARGS)
                                807                 :                : {
                                808                 :                :     Datum       result;
                                809                 :                :     TupleDesc   tupledesc;
                                810                 :                :     HeapTuple   tuple;
                                811                 :                :     Datum       values[NUM_BUFFERCACHE_EVICT_ALL_ELEM];
                                812                 :              2 :     bool        nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0};
                                813                 :                : 
                                814                 :              2 :     int32       buffers_evicted = 0;
                                815                 :              2 :     int32       buffers_flushed = 0;
                                816                 :              2 :     int32       buffers_skipped = 0;
                                817                 :                : 
                                818         [ -  + ]:              2 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
  252 andres@anarazel.de        819         [ #  # ]:UBC           0 :         elog(ERROR, "return type must be a row type");
                                820                 :                : 
  252 andres@anarazel.de        821                 :CBC           2 :     pg_buffercache_superuser_check("pg_buffercache_evict_all");
                                822                 :                : 
                                823                 :              1 :     EvictAllUnpinnedBuffers(&buffers_evicted, &buffers_flushed,
                                824                 :                :                             &buffers_skipped);
                                825                 :                : 
                                826                 :              1 :     values[0] = Int32GetDatum(buffers_evicted);
                                827                 :              1 :     values[1] = Int32GetDatum(buffers_flushed);
                                828                 :              1 :     values[2] = Int32GetDatum(buffers_skipped);
                                829                 :                : 
                                830                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                831                 :              1 :     result = HeapTupleGetDatum(tuple);
                                832                 :                : 
                                833                 :              1 :     PG_RETURN_DATUM(result);
                                834                 :                : }
                                835                 :                : 
                                836                 :                : /*
                                837                 :                :  * Try to mark a shared buffer as dirty.
                                838                 :                :  */
                                839                 :                : Datum
   18 michael@paquier.xyz       840                 :GNC           5 : pg_buffercache_mark_dirty(PG_FUNCTION_ARGS)
                                841                 :                : {
                                842                 :                : 
                                843                 :                :     Datum       result;
                                844                 :                :     TupleDesc   tupledesc;
                                845                 :                :     HeapTuple   tuple;
                                846                 :                :     Datum       values[NUM_BUFFERCACHE_MARK_DIRTY_ELEM];
                                847                 :              5 :     bool        nulls[NUM_BUFFERCACHE_MARK_DIRTY_ELEM] = {0};
                                848                 :                : 
                                849                 :              5 :     Buffer      buf = PG_GETARG_INT32(0);
                                850                 :                :     bool        buffer_already_dirty;
                                851                 :                : 
                                852         [ -  + ]:              5 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
   18 michael@paquier.xyz       853         [ #  # ]:UNC           0 :         elog(ERROR, "return type must be a row type");
                                854                 :                : 
   18 michael@paquier.xyz       855                 :GNC           5 :     pg_buffercache_superuser_check("pg_buffercache_mark_dirty");
                                856                 :                : 
                                857   [ +  +  +  + ]:              4 :     if (buf < 1 || buf > NBuffers)
                                858         [ +  - ]:              3 :         elog(ERROR, "bad buffer ID: %d", buf);
                                859                 :                : 
                                860                 :              1 :     values[0] = BoolGetDatum(MarkDirtyUnpinnedBuffer(buf, &buffer_already_dirty));
                                861                 :              1 :     values[1] = BoolGetDatum(buffer_already_dirty);
                                862                 :                : 
                                863                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                864                 :              1 :     result = HeapTupleGetDatum(tuple);
                                865                 :                : 
                                866                 :              1 :     PG_RETURN_DATUM(result);
                                867                 :                : }
                                868                 :                : 
                                869                 :                : /*
                                870                 :                :  * Try to mark all the shared buffers of a relation as dirty.
                                871                 :                :  */
                                872                 :                : Datum
                                873                 :              3 : pg_buffercache_mark_dirty_relation(PG_FUNCTION_ARGS)
                                874                 :                : {
                                875                 :                :     Datum       result;
                                876                 :                :     TupleDesc   tupledesc;
                                877                 :                :     HeapTuple   tuple;
                                878                 :                :     Datum       values[NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM];
                                879                 :              3 :     bool        nulls[NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM] = {0};
                                880                 :                : 
                                881                 :                :     Oid         relOid;
                                882                 :                :     Relation    rel;
                                883                 :                : 
                                884                 :              3 :     int32       buffers_already_dirty = 0;
                                885                 :              3 :     int32       buffers_dirtied = 0;
                                886                 :              3 :     int32       buffers_skipped = 0;
                                887                 :                : 
                                888         [ -  + ]:              3 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
   18 michael@paquier.xyz       889         [ #  # ]:UNC           0 :         elog(ERROR, "return type must be a row type");
                                890                 :                : 
   18 michael@paquier.xyz       891                 :GNC           3 :     pg_buffercache_superuser_check("pg_buffercache_mark_dirty_relation");
                                892                 :                : 
                                893                 :              2 :     relOid = PG_GETARG_OID(0);
                                894                 :                : 
                                895                 :              2 :     rel = relation_open(relOid, AccessShareLock);
                                896                 :                : 
                                897         [ +  + ]:              2 :     if (RelationUsesLocalBuffers(rel))
                                898         [ +  - ]:              1 :         ereport(ERROR,
                                899                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                900                 :                :                  errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
                                901                 :                :                         "pg_buffercache_mark_dirty_relation")));
                                902                 :                : 
                                903                 :              1 :     MarkDirtyRelUnpinnedBuffers(rel, &buffers_dirtied, &buffers_already_dirty,
                                904                 :                :                                 &buffers_skipped);
                                905                 :                : 
                                906                 :              1 :     relation_close(rel, AccessShareLock);
                                907                 :                : 
                                908                 :              1 :     values[0] = Int32GetDatum(buffers_dirtied);
                                909                 :              1 :     values[1] = Int32GetDatum(buffers_already_dirty);
                                910                 :              1 :     values[2] = Int32GetDatum(buffers_skipped);
                                911                 :                : 
                                912                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                913                 :              1 :     result = HeapTupleGetDatum(tuple);
                                914                 :                : 
                                915                 :              1 :     PG_RETURN_DATUM(result);
                                916                 :                : }
                                917                 :                : 
                                918                 :                : /*
                                919                 :                :  * Try to mark all the shared buffers as dirty.
                                920                 :                :  */
                                921                 :                : Datum
                                922                 :              2 : pg_buffercache_mark_dirty_all(PG_FUNCTION_ARGS)
                                923                 :                : {
                                924                 :                :     Datum       result;
                                925                 :                :     TupleDesc   tupledesc;
                                926                 :                :     HeapTuple   tuple;
                                927                 :                :     Datum       values[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM];
                                928                 :              2 :     bool        nulls[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM] = {0};
                                929                 :                : 
                                930                 :              2 :     int32       buffers_already_dirty = 0;
                                931                 :              2 :     int32       buffers_dirtied = 0;
                                932                 :              2 :     int32       buffers_skipped = 0;
                                933                 :                : 
                                934         [ -  + ]:              2 :     if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
   18 michael@paquier.xyz       935         [ #  # ]:UNC           0 :         elog(ERROR, "return type must be a row type");
                                936                 :                : 
   18 michael@paquier.xyz       937                 :GNC           2 :     pg_buffercache_superuser_check("pg_buffercache_mark_dirty_all");
                                938                 :                : 
                                939                 :              1 :     MarkDirtyAllUnpinnedBuffers(&buffers_dirtied, &buffers_already_dirty,
                                940                 :                :                                 &buffers_skipped);
                                941                 :                : 
                                942                 :              1 :     values[0] = Int32GetDatum(buffers_dirtied);
                                943                 :              1 :     values[1] = Int32GetDatum(buffers_already_dirty);
                                944                 :              1 :     values[2] = Int32GetDatum(buffers_skipped);
                                945                 :                : 
                                946                 :              1 :     tuple = heap_form_tuple(tupledesc, values, nulls);
                                947                 :              1 :     result = HeapTupleGetDatum(tuple);
                                948                 :                : 
                                949                 :              1 :     PG_RETURN_DATUM(result);
                                950                 :                : }
        

Generated by: LCOV version 2.4-beta