LCOV - code coverage report
Current view: top level - src/test/modules/test_custom_stats - test_custom_var_stats.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 239 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 15 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*------------------------------------------------------------------------------------
       2              :  *
       3              :  * test_custom_var_stats.c
       4              :  *              Test module for variable-sized custom pgstats
       5              :  *
       6              :  * Copyright (c) 2025-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *              src/test/modules/test_custom_var_stats/test_custom_var_stats.c
      10              :  *
      11              :  * ------------------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "common/hashfn.h"
      16              : #include "funcapi.h"
      17              : #include "storage/dsm_registry.h"
      18              : #include "utils/builtins.h"
      19              : #include "utils/pgstat_internal.h"
      20              : 
      21            0 : PG_MODULE_MAGIC_EXT(
      22              :                                         .name = "test_custom_var_stats",
      23              :                                         .version = PG_VERSION
      24              : );
      25              : 
      26              : #define TEST_CUSTOM_VAR_MAGIC_NUMBER (0xBEEFBEEF)
      27              : 
      28              : /*--------------------------------------------------------------------------
      29              :  * Macros and constants
      30              :  *--------------------------------------------------------------------------
      31              :  */
      32              : 
      33              : /*
      34              :  * Kind ID for test_custom_var_stats statistics.
      35              :  */
      36              : #define PGSTAT_KIND_TEST_CUSTOM_VAR_STATS 25
      37              : 
      38              : /* File paths for auxiliary data serialization */
      39              : #define TEST_CUSTOM_AUX_DATA_DESC "pg_stat/test_custom_var_stats_desc.stats"
      40              : 
      41              : /*
      42              :  * Hash statistic name to generate entry index for pgstat lookup.
      43              :  */
      44              : #define PGSTAT_CUSTOM_VAR_STATS_IDX(name) hash_bytes_extended((const unsigned char *) name, strlen(name), 0)
      45              : 
      46              : /*--------------------------------------------------------------------------
      47              :  * Type definitions
      48              :  *--------------------------------------------------------------------------
      49              :  */
      50              : 
      51              : /* Backend-local pending statistics before flush to shared memory */
      52              : typedef struct PgStat_StatCustomVarEntry
      53              : {
      54              :         PgStat_Counter numcalls;        /* times statistic was incremented */
      55              : } PgStat_StatCustomVarEntry;
      56              : 
      57              : /* Shared memory statistics entry visible to all backends */
      58              : typedef struct PgStatShared_CustomVarEntry
      59              : {
      60              :         PgStatShared_Common header; /* standard pgstat entry header */
      61              :         PgStat_StatCustomVarEntry stats;        /* custom statistics data */
      62              :         dsa_pointer description;        /* pointer to description string in DSA */
      63              : } PgStatShared_CustomVarEntry;
      64              : 
      65              : /*--------------------------------------------------------------------------
      66              :  * Global Variables
      67              :  *--------------------------------------------------------------------------
      68              :  */
      69              : 
      70              : /* File handle for auxiliary data serialization */
      71              : static FILE *fd_description = NULL;
      72              : 
      73              : /* Current write offset in fd_description file */
      74              : static pgoff_t fd_description_offset = 0;
      75              : 
      76              : /* DSA area for storing variable-length description strings */
      77              : static dsa_area *custom_stats_description_dsa = NULL;
      78              : 
      79              : /*--------------------------------------------------------------------------
      80              :  * Function prototypes
      81              :  *--------------------------------------------------------------------------
      82              :  */
      83              : 
      84              : /* Flush callback: merge pending stats into shared memory */
      85              : static bool test_custom_stats_var_flush_pending_cb(PgStat_EntryRef *entry_ref,
      86              :                                                                                                    bool nowait);
      87              : 
      88              : /* Serialization callback: write auxiliary entry data */
      89              : static void test_custom_stats_var_to_serialized_data(const PgStat_HashKey *key,
      90              :                                                                                                          const PgStatShared_Common *header,
      91              :                                                                                                          FILE *statfile);
      92              : 
      93              : /* Deserialization callback: read auxiliary entry data */
      94              : static bool test_custom_stats_var_from_serialized_data(const PgStat_HashKey *key,
      95              :                                                                                                            PgStatShared_Common *header,
      96              :                                                                                                            FILE *statfile);
      97              : 
      98              : /* Finish callback: end of statistics file operations */
      99              : static void test_custom_stats_var_finish(PgStat_StatsFileOp status);
     100              : 
     101              : /*--------------------------------------------------------------------------
     102              :  * Custom kind configuration
     103              :  *--------------------------------------------------------------------------
     104              :  */
     105              : 
     106              : static const PgStat_KindInfo custom_stats = {
     107              :         .name = "test_custom_var_stats",
     108              :         .fixed_amount = false,          /* variable number of entries */
     109              :         .write_to_file = true,          /* persist across restarts */
     110              :         .track_entry_count = true,      /* count active entries */
     111              :         .accessed_across_databases = true,      /* global statistics */
     112              :         .shared_size = sizeof(PgStatShared_CustomVarEntry),
     113              :         .shared_data_off = offsetof(PgStatShared_CustomVarEntry, stats),
     114              :         .shared_data_len = sizeof(((PgStatShared_CustomVarEntry *) 0)->stats),
     115              :         .pending_size = sizeof(PgStat_StatCustomVarEntry),
     116              :         .flush_pending_cb = test_custom_stats_var_flush_pending_cb,
     117              :         .to_serialized_data = test_custom_stats_var_to_serialized_data,
     118              :         .from_serialized_data = test_custom_stats_var_from_serialized_data,
     119              :         .finish = test_custom_stats_var_finish,
     120              : };
     121              : 
     122              : /*--------------------------------------------------------------------------
     123              :  * Module initialization
     124              :  *--------------------------------------------------------------------------
     125              :  */
     126              : 
     127              : void
     128            0 : _PG_init(void)
     129              : {
     130              :         /* Must be loaded via shared_preload_libraries */
     131            0 :         if (!process_shared_preload_libraries_in_progress)
     132            0 :                 return;
     133              : 
     134              :         /* Register custom statistics kind */
     135            0 :         pgstat_register_kind(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, &custom_stats);
     136            0 : }
     137              : 
     138              : /*--------------------------------------------------------------------------
     139              :  * Statistics callback functions
     140              :  *--------------------------------------------------------------------------
     141              :  */
     142              : 
     143              : /*
     144              :  * test_custom_stats_var_flush_pending_cb
     145              :  *              Merge pending backend statistics into shared memory
     146              :  *
     147              :  * Called by pgstat collector to flush accumulated local statistics
     148              :  * to shared memory where other backends can read them.
     149              :  *
     150              :  * Returns false only if nowait=true and lock acquisition fails.
     151              :  */
     152              : static bool
     153            0 : test_custom_stats_var_flush_pending_cb(PgStat_EntryRef *entry_ref, bool nowait)
     154              : {
     155            0 :         PgStat_StatCustomVarEntry *pending_entry;
     156            0 :         PgStatShared_CustomVarEntry *shared_entry;
     157              : 
     158            0 :         pending_entry = (PgStat_StatCustomVarEntry *) entry_ref->pending;
     159            0 :         shared_entry = (PgStatShared_CustomVarEntry *) entry_ref->shared_stats;
     160              : 
     161            0 :         if (!pgstat_lock_entry(entry_ref, nowait))
     162            0 :                 return false;
     163              : 
     164              :         /* Add pending counts to shared totals */
     165            0 :         shared_entry->stats.numcalls += pending_entry->numcalls;
     166              : 
     167            0 :         pgstat_unlock_entry(entry_ref);
     168              : 
     169            0 :         return true;
     170            0 : }
     171              : 
     172              : /*
     173              :  * test_custom_stats_var_to_serialized_data() -
     174              :  *
     175              :  * Serialize auxiliary data (descriptions) for custom statistics entries
     176              :  * to a secondary statistics file. This is called while writing the statistics
     177              :  * to disk.
     178              :  *
     179              :  * This callback writes a mix of data within the main pgstats file and a
     180              :  * secondary statistics file.  The following data is written to the main file for
     181              :  * each entry:
     182              :  * - An arbitrary magic number.
     183              :  * - An offset.  This is used to know the location we need to look at
     184              :  * to retrieve the information from the second file.
     185              :  *
     186              :  * The following data is written to the secondary statistics file:
     187              :  * - The entry key, cross-checked with the data from the main file
     188              :  * when reloaded.
     189              :  * - The length of the description.
     190              :  * - The description data itself.
     191              :  */
     192              : static void
     193            0 : test_custom_stats_var_to_serialized_data(const PgStat_HashKey *key,
     194              :                                                                                  const PgStatShared_Common *header,
     195              :                                                                                  FILE *statfile)
     196              : {
     197            0 :         char       *description;
     198            0 :         size_t          len;
     199            0 :         const PgStatShared_CustomVarEntry *entry = (const PgStatShared_CustomVarEntry *) header;
     200            0 :         bool            found;
     201            0 :         uint32          magic_number = TEST_CUSTOM_VAR_MAGIC_NUMBER;
     202              : 
     203              :         /*
     204              :          * First mark the main file with a magic number, keeping a trace that some
     205              :          * auxiliary data will exist in the secondary statistics file.
     206              :          */
     207            0 :         pgstat_write_chunk_s(statfile, &magic_number);
     208              : 
     209              :         /* Open statistics file for writing. */
     210            0 :         if (!fd_description)
     211              :         {
     212            0 :                 fd_description = AllocateFile(TEST_CUSTOM_AUX_DATA_DESC, PG_BINARY_W);
     213            0 :                 if (fd_description == NULL)
     214              :                 {
     215            0 :                         ereport(LOG,
     216              :                                         (errcode_for_file_access(),
     217              :                                          errmsg("could not open statistics file \"%s\" for writing: %m",
     218              :                                                         TEST_CUSTOM_AUX_DATA_DESC)));
     219            0 :                         return;
     220              :                 }
     221              : 
     222              :                 /* Initialize offset for secondary statistics file. */
     223            0 :                 fd_description_offset = 0;
     224            0 :         }
     225              : 
     226              :         /* Write offset to the main data file */
     227            0 :         pgstat_write_chunk_s(statfile, &fd_description_offset);
     228              : 
     229              :         /*
     230              :          * First write the entry key to the secondary statistics file.  This will
     231              :          * be cross-checked with the key read from main stats file at loading
     232              :          * time.
     233              :          */
     234            0 :         pgstat_write_chunk_s(fd_description, (PgStat_HashKey *) key);
     235            0 :         fd_description_offset += sizeof(PgStat_HashKey);
     236              : 
     237            0 :         if (!custom_stats_description_dsa)
     238            0 :                 custom_stats_description_dsa = GetNamedDSA("test_custom_stat_dsa", &found);
     239              : 
     240              :         /* Handle entries without descriptions */
     241            0 :         if (!DsaPointerIsValid(entry->description) || !custom_stats_description_dsa)
     242              :         {
     243              :                 /* length to description file */
     244            0 :                 len = 0;
     245            0 :                 pgstat_write_chunk_s(fd_description, &len);
     246            0 :                 fd_description_offset += sizeof(size_t);
     247            0 :                 return;
     248              :         }
     249              : 
     250              :         /*
     251              :          * Retrieve description from DSA, then write the length followed by the
     252              :          * description.
     253              :          */
     254            0 :         description = dsa_get_address(custom_stats_description_dsa,
     255            0 :                                                                   entry->description);
     256            0 :         len = strlen(description) + 1;
     257            0 :         pgstat_write_chunk_s(fd_description, &len);
     258            0 :         pgstat_write_chunk(fd_description, description, len);
     259              : 
     260              :         /*
     261              :          * Update offset for next entry, counting for the length (size_t) of the
     262              :          * description and the description contents.
     263              :          */
     264            0 :         fd_description_offset += len + sizeof(size_t);
     265            0 : }
     266              : 
     267              : /*
     268              :  * test_custom_stats_var_from_serialized_data() -
     269              :  *
     270              :  * Read auxiliary data (descriptions) for custom statistics entries from
     271              :  * the secondary statistics file.  This is called while loading the statistics
     272              :  * at startup.
     273              :  *
     274              :  * See the top of test_custom_stats_var_to_serialized_data() for a
     275              :  * detailed description of the data layout read here.
     276              :  */
     277              : static bool
     278            0 : test_custom_stats_var_from_serialized_data(const PgStat_HashKey *key,
     279              :                                                                                    PgStatShared_Common *header,
     280              :                                                                                    FILE *statfile)
     281              : {
     282            0 :         PgStatShared_CustomVarEntry *entry;
     283            0 :         dsa_pointer dp;
     284            0 :         size_t          len;
     285            0 :         pgoff_t         offset;
     286            0 :         char       *buffer;
     287            0 :         bool            found;
     288            0 :         uint32          magic_number = 0;
     289            0 :         PgStat_HashKey file_key;
     290              : 
     291              :         /* Check the magic number first, in the main file. */
     292            0 :         if (!pgstat_read_chunk_s(statfile, &magic_number))
     293              :         {
     294            0 :                 elog(WARNING, "failed to read magic number from statistics file");
     295            0 :                 return false;
     296              :         }
     297              : 
     298            0 :         if (magic_number != TEST_CUSTOM_VAR_MAGIC_NUMBER)
     299              :         {
     300            0 :                 elog(WARNING, "found magic number %u from statistics file, should be %u",
     301              :                          magic_number, TEST_CUSTOM_VAR_MAGIC_NUMBER);
     302            0 :                 return false;
     303              :         }
     304              : 
     305              :         /*
     306              :          * Read the offset from the main stats file, to be able to read the
     307              :          * auxiliary data from the secondary statistics file.
     308              :          */
     309            0 :         if (!pgstat_read_chunk_s(statfile, &offset))
     310              :         {
     311            0 :                 elog(WARNING, "failed to read metadata offset from statistics file");
     312            0 :                 return false;
     313              :         }
     314              : 
     315              :         /* Open statistics file for reading if not already open */
     316            0 :         if (!fd_description)
     317              :         {
     318            0 :                 fd_description = AllocateFile(TEST_CUSTOM_AUX_DATA_DESC, PG_BINARY_R);
     319            0 :                 if (fd_description == NULL)
     320              :                 {
     321            0 :                         if (errno != ENOENT)
     322            0 :                                 ereport(LOG,
     323              :                                                 (errcode_for_file_access(),
     324              :                                                  errmsg("could not open statistics file \"%s\" for reading: %m",
     325              :                                                                 TEST_CUSTOM_AUX_DATA_DESC)));
     326            0 :                         pgstat_reset_of_kind(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS);
     327            0 :                         return false;
     328              :                 }
     329            0 :         }
     330              : 
     331              :         /* Read data from the secondary statistics file, at the specified offset */
     332            0 :         if (fseeko(fd_description, offset, SEEK_SET) != 0)
     333              :         {
     334            0 :                 elog(WARNING, "could not seek in file \"%s\": %m",
     335              :                          TEST_CUSTOM_AUX_DATA_DESC);
     336            0 :                 return false;
     337              :         }
     338              : 
     339              :         /* Read the hash key from the secondary statistics file */
     340            0 :         if (!pgstat_read_chunk_s(fd_description, &file_key))
     341              :         {
     342            0 :                 elog(WARNING, "failed to read hash key from file");
     343            0 :                 return false;
     344              :         }
     345              : 
     346              :         /* Check key consistency */
     347            0 :         if (file_key.kind != key->kind ||
     348            0 :                 file_key.dboid != key->dboid ||
     349            0 :                 file_key.objid != key->objid)
     350              :         {
     351            0 :                 elog(WARNING, "found entry key %u/%u/%" PRIu64 " not matching with %u/%u/%" PRIu64,
     352              :                          file_key.kind, file_key.dboid, file_key.objid,
     353              :                          key->kind, key->dboid, key->objid);
     354            0 :                 return false;
     355              :         }
     356              : 
     357            0 :         entry = (PgStatShared_CustomVarEntry *) header;
     358              : 
     359              :         /* Read the description length and its data */
     360            0 :         if (!pgstat_read_chunk_s(fd_description, &len))
     361              :         {
     362            0 :                 elog(WARNING, "failed to read metadata length from statistics file");
     363            0 :                 return false;
     364              :         }
     365              : 
     366              :         /* Handle empty descriptions */
     367            0 :         if (len == 0)
     368              :         {
     369            0 :                 entry->description = InvalidDsaPointer;
     370            0 :                 return true;
     371              :         }
     372              : 
     373              :         /* Initialize DSA if needed */
     374            0 :         if (!custom_stats_description_dsa)
     375            0 :                 custom_stats_description_dsa = GetNamedDSA("test_custom_stat_dsa", &found);
     376              : 
     377            0 :         if (!custom_stats_description_dsa)
     378              :         {
     379            0 :                 elog(WARNING, "could not access DSA for custom statistics descriptions");
     380            0 :                 return false;
     381              :         }
     382              : 
     383            0 :         buffer = palloc(len);
     384            0 :         if (!pgstat_read_chunk(fd_description, buffer, len))
     385              :         {
     386            0 :                 pfree(buffer);
     387            0 :                 elog(WARNING, "failed to read description from file");
     388            0 :                 return false;
     389              :         }
     390              : 
     391              :         /* Allocate space in DSA and copy the description */
     392            0 :         dp = dsa_allocate(custom_stats_description_dsa, len);
     393            0 :         memcpy(dsa_get_address(custom_stats_description_dsa, dp), buffer, len);
     394            0 :         entry->description = dp;
     395            0 :         pfree(buffer);
     396              : 
     397            0 :         return true;
     398            0 : }
     399              : 
     400              : /*
     401              :  * test_custom_stats_var_finish() -
     402              :  *
     403              :  * Cleanup function called at the end of statistics file operations.
     404              :  * Handles closing files and cleanup based on the operation type.
     405              :  */
     406              : static void
     407            0 : test_custom_stats_var_finish(PgStat_StatsFileOp status)
     408              : {
     409            0 :         switch (status)
     410              :         {
     411              :                 case STATS_WRITE:
     412            0 :                         if (!fd_description)
     413            0 :                                 return;
     414              : 
     415            0 :                         fd_description_offset = 0;
     416              : 
     417              :                         /* Check for write errors and cleanup if necessary */
     418            0 :                         if (ferror(fd_description))
     419              :                         {
     420            0 :                                 ereport(LOG,
     421              :                                                 (errcode_for_file_access(),
     422              :                                                  errmsg("could not write to file \"%s\": %m",
     423              :                                                                 TEST_CUSTOM_AUX_DATA_DESC)));
     424            0 :                                 FreeFile(fd_description);
     425            0 :                                 unlink(TEST_CUSTOM_AUX_DATA_DESC);
     426            0 :                         }
     427            0 :                         else if (FreeFile(fd_description) < 0)
     428              :                         {
     429            0 :                                 ereport(LOG,
     430              :                                                 (errcode_for_file_access(),
     431              :                                                  errmsg("could not close file \"%s\": %m",
     432              :                                                                 TEST_CUSTOM_AUX_DATA_DESC)));
     433            0 :                                 unlink(TEST_CUSTOM_AUX_DATA_DESC);
     434            0 :                         }
     435            0 :                         break;
     436              : 
     437              :                 case STATS_READ:
     438            0 :                         if (fd_description)
     439            0 :                                 FreeFile(fd_description);
     440              : 
     441              :                         /* Remove the file after reading */
     442            0 :                         elog(DEBUG2, "removing file \"%s\"", TEST_CUSTOM_AUX_DATA_DESC);
     443            0 :                         unlink(TEST_CUSTOM_AUX_DATA_DESC);
     444            0 :                         break;
     445              : 
     446              :                 case STATS_DISCARD:
     447              :                         {
     448            0 :                                 int                     ret;
     449              : 
     450              :                                 /* Attempt to remove the file */
     451            0 :                                 ret = unlink(TEST_CUSTOM_AUX_DATA_DESC);
     452            0 :                                 if (ret != 0)
     453              :                                 {
     454            0 :                                         if (errno == ENOENT)
     455            0 :                                                 elog(LOG,
     456              :                                                          "didn't need to unlink file \"%s\" - didn't exist",
     457              :                                                          TEST_CUSTOM_AUX_DATA_DESC);
     458              :                                         else
     459            0 :                                                 ereport(LOG,
     460              :                                                                 (errcode_for_file_access(),
     461              :                                                                  errmsg("could not unlink file \"%s\": %m",
     462              :                                                                                 TEST_CUSTOM_AUX_DATA_DESC)));
     463            0 :                                 }
     464              :                                 else
     465              :                                 {
     466            0 :                                         ereport(LOG,
     467              :                                                         (errmsg_internal("unlinked file \"%s\"",
     468              :                                                                                          TEST_CUSTOM_AUX_DATA_DESC)));
     469              :                                 }
     470            0 :                         }
     471            0 :                         break;
     472              :         }
     473              : 
     474            0 :         fd_description = NULL;
     475            0 : }
     476              : 
     477              : /*--------------------------------------------------------------------------
     478              :  * Helper functions
     479              :  *--------------------------------------------------------------------------
     480              :  */
     481              : 
     482              : /*
     483              :  * test_custom_stats_var_fetch_entry
     484              :  *              Look up custom statistic by name
     485              :  *
     486              :  * Returns statistics entry from shared memory, or NULL if not found.
     487              :  */
     488              : static PgStat_StatCustomVarEntry *
     489            0 : test_custom_stats_var_fetch_entry(const char *stat_name)
     490              : {
     491              :         /* Fetch entry by hashed name */
     492            0 :         return (PgStat_StatCustomVarEntry *)
     493            0 :                 pgstat_fetch_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS,
     494              :                                                    InvalidOid,
     495            0 :                                                    PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name));
     496              : }
     497              : 
     498              : /*--------------------------------------------------------------------------
     499              :  * SQL-callable functions
     500              :  *--------------------------------------------------------------------------
     501              :  */
     502              : 
     503              : /*
     504              :  * test_custom_stats_var_create
     505              :  *              Create new custom statistic entry
     506              :  *
     507              :  * Initializes a statistics entry with the given name and description.
     508              :  */
     509            0 : PG_FUNCTION_INFO_V1(test_custom_stats_var_create);
     510              : Datum
     511            0 : test_custom_stats_var_create(PG_FUNCTION_ARGS)
     512              : {
     513            0 :         PgStat_EntryRef *entry_ref;
     514            0 :         PgStatShared_CustomVarEntry *shared_entry;
     515            0 :         char       *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     516            0 :         char       *description = text_to_cstring(PG_GETARG_TEXT_PP(1));
     517            0 :         dsa_pointer dp = InvalidDsaPointer;
     518            0 :         bool            found;
     519              : 
     520              :         /* Validate name length first */
     521            0 :         if (strlen(stat_name) >= NAMEDATALEN)
     522            0 :                 ereport(ERROR,
     523              :                                 (errcode(ERRCODE_NAME_TOO_LONG),
     524              :                                  errmsg("custom statistic name \"%s\" is too long", stat_name),
     525              :                                  errdetail("Name must be less than %d characters.", NAMEDATALEN)));
     526              : 
     527              :         /* Initialize DSA and description provided */
     528            0 :         if (!custom_stats_description_dsa)
     529            0 :                 custom_stats_description_dsa = GetNamedDSA("test_custom_stat_dsa", &found);
     530              : 
     531            0 :         if (!custom_stats_description_dsa)
     532            0 :                 ereport(ERROR,
     533              :                                 (errmsg("could not access DSA for custom statistics descriptions")));
     534              : 
     535              :         /* Allocate space in DSA and copy description */
     536            0 :         dp = dsa_allocate(custom_stats_description_dsa, strlen(description) + 1);
     537            0 :         memcpy(dsa_get_address(custom_stats_description_dsa, dp),
     538              :                    description,
     539              :                    strlen(description) + 1);
     540              : 
     541              :         /* Create or get existing entry */
     542            0 :         entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
     543            0 :                                                                                         PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), true);
     544              : 
     545            0 :         if (!entry_ref)
     546            0 :                 PG_RETURN_VOID();
     547              : 
     548            0 :         shared_entry = (PgStatShared_CustomVarEntry *) entry_ref->shared_stats;
     549              : 
     550              :         /* Zero-initialize statistics */
     551            0 :         memset(&shared_entry->stats, 0, sizeof(shared_entry->stats));
     552              : 
     553              :         /* Store description pointer */
     554            0 :         shared_entry->description = dp;
     555              : 
     556            0 :         pgstat_unlock_entry(entry_ref);
     557              : 
     558            0 :         PG_RETURN_VOID();
     559            0 : }
     560              : 
     561              : /*
     562              :  * test_custom_stats_var_update
     563              :  *              Increment custom statistic counter
     564              :  *
     565              :  * Increments call count in backend-local memory.  Changes are flushed
     566              :  * to shared memory by the statistics collector.
     567              :  */
     568            0 : PG_FUNCTION_INFO_V1(test_custom_stats_var_update);
     569              : Datum
     570            0 : test_custom_stats_var_update(PG_FUNCTION_ARGS)
     571              : {
     572            0 :         char       *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     573            0 :         PgStat_EntryRef *entry_ref;
     574            0 :         PgStat_StatCustomVarEntry *pending_entry;
     575              : 
     576              :         /* Get pending entry in local memory */
     577            0 :         entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
     578            0 :                                                                                   PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), NULL);
     579              : 
     580            0 :         pending_entry = (PgStat_StatCustomVarEntry *) entry_ref->pending;
     581            0 :         pending_entry->numcalls++;
     582              : 
     583            0 :         PG_RETURN_VOID();
     584            0 : }
     585              : 
     586              : /*
     587              :  * test_custom_stats_var_drop
     588              :  *              Remove custom statistic entry
     589              :  *
     590              :  * Drops the named statistic from shared memory.
     591              :  */
     592            0 : PG_FUNCTION_INFO_V1(test_custom_stats_var_drop);
     593              : Datum
     594            0 : test_custom_stats_var_drop(PG_FUNCTION_ARGS)
     595              : {
     596            0 :         char       *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     597              : 
     598              :         /* Drop entry and request GC if the entry could not be freed */
     599            0 :         if (!pgstat_drop_entry(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
     600            0 :                                                    PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name)))
     601            0 :                 pgstat_request_entry_refs_gc();
     602              : 
     603            0 :         PG_RETURN_VOID();
     604            0 : }
     605              : 
     606              : /*
     607              :  * test_custom_stats_var_report
     608              :  *              Retrieve custom statistic values
     609              :  *
     610              :  * Returns single row with statistic name, call count, and description if the
     611              :  * statistic exists, otherwise returns no rows.
     612              :  */
     613            0 : PG_FUNCTION_INFO_V1(test_custom_stats_var_report);
     614              : Datum
     615            0 : test_custom_stats_var_report(PG_FUNCTION_ARGS)
     616              : {
     617            0 :         FuncCallContext *funcctx;
     618            0 :         char       *stat_name;
     619            0 :         PgStat_StatCustomVarEntry *stat_entry;
     620              : 
     621            0 :         if (SRF_IS_FIRSTCALL())
     622              :         {
     623            0 :                 TupleDesc       tupdesc;
     624            0 :                 MemoryContext oldcontext;
     625              : 
     626              :                 /* Initialize SRF context */
     627            0 :                 funcctx = SRF_FIRSTCALL_INIT();
     628            0 :                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     629              : 
     630              :                 /* Get composite return type */
     631            0 :                 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     632            0 :                         elog(ERROR, "test_custom_stats_var_report: return type is not composite");
     633              : 
     634            0 :                 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
     635            0 :                 funcctx->max_calls = 1; /* single row result */
     636              : 
     637            0 :                 MemoryContextSwitchTo(oldcontext);
     638            0 :         }
     639              : 
     640            0 :         funcctx = SRF_PERCALL_SETUP();
     641              : 
     642            0 :         if (funcctx->call_cntr < funcctx->max_calls)
     643              :         {
     644            0 :                 Datum           values[3];
     645            0 :                 bool            nulls[3] = {false, false, false};
     646            0 :                 HeapTuple       tuple;
     647            0 :                 PgStat_EntryRef *entry_ref;
     648            0 :                 PgStatShared_CustomVarEntry *shared_entry;
     649            0 :                 char       *description = NULL;
     650            0 :                 bool            found;
     651              : 
     652            0 :                 stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     653            0 :                 stat_entry = test_custom_stats_var_fetch_entry(stat_name);
     654              : 
     655              :                 /* Return row only if entry exists */
     656            0 :                 if (stat_entry)
     657              :                 {
     658              :                         /* Get entry ref to access shared entry */
     659            0 :                         entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
     660            0 :                                                                                          PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), false, NULL);
     661              : 
     662            0 :                         if (entry_ref)
     663              :                         {
     664            0 :                                 shared_entry = (PgStatShared_CustomVarEntry *) entry_ref->shared_stats;
     665              : 
     666              :                                 /* Get description from DSA if available */
     667            0 :                                 if (DsaPointerIsValid(shared_entry->description))
     668              :                                 {
     669            0 :                                         if (!custom_stats_description_dsa)
     670            0 :                                                 custom_stats_description_dsa = GetNamedDSA("test_custom_stat_dsa", &found);
     671              : 
     672            0 :                                         if (custom_stats_description_dsa)
     673            0 :                                                 description = dsa_get_address(custom_stats_description_dsa, shared_entry->description);
     674            0 :                                 }
     675            0 :                         }
     676              : 
     677            0 :                         values[0] = PointerGetDatum(cstring_to_text(stat_name));
     678            0 :                         values[1] = Int64GetDatum(stat_entry->numcalls);
     679              : 
     680            0 :                         if (description)
     681            0 :                                 values[2] = PointerGetDatum(cstring_to_text(description));
     682              :                         else
     683            0 :                                 nulls[2] = true;
     684              : 
     685            0 :                         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     686            0 :                         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
     687            0 :                 }
     688            0 :         }
     689              : 
     690            0 :         SRF_RETURN_DONE(funcctx);
     691            0 : }
        

Generated by: LCOV version 2.3.2-1