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

Generated by: LCOV version 2.0-1