LCOV - code coverage report
Current view: top level - contrib/pg_stash_advice - stashfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.4 % 107 101
Test Date: 2026-04-07 10:16:37 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * stashfuncs.c
       4              :  *    SQL interface to pg_stash_advice
       5              :  *
       6              :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *    contrib/pg_stash_advice/stashfuncs.c
       9              :  *
      10              :  *-------------------------------------------------------------------------
      11              :  */
      12              : #include "postgres.h"
      13              : 
      14              : #include "common/hashfn.h"
      15              : #include "fmgr.h"
      16              : #include "funcapi.h"
      17              : #include "pg_stash_advice.h"
      18              : #include "utils/builtins.h"
      19              : #include "utils/tuplestore.h"
      20              : 
      21            3 : PG_FUNCTION_INFO_V1(pg_create_advice_stash);
      22            2 : PG_FUNCTION_INFO_V1(pg_drop_advice_stash);
      23            2 : PG_FUNCTION_INFO_V1(pg_get_advice_stash_contents);
      24            2 : PG_FUNCTION_INFO_V1(pg_get_advice_stashes);
      25            2 : PG_FUNCTION_INFO_V1(pg_set_stashed_advice);
      26              : 
      27              : typedef struct pgsa_stash_count
      28              : {
      29              :     uint32      status;
      30              :     uint64      pgsa_stash_id;
      31              :     int64       num_entries;
      32              : } pgsa_stash_count;
      33              : 
      34              : #define SH_PREFIX pgsa_stash_count_table
      35              : #define SH_ELEMENT_TYPE pgsa_stash_count
      36              : #define SH_KEY_TYPE uint64
      37              : #define SH_KEY pgsa_stash_id
      38              : #define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(uint64))
      39              : #define SH_EQUAL(tb, a, b) (a == b)
      40              : #define SH_SCOPE static inline
      41              : #define SH_DEFINE
      42              : #define SH_DECLARE
      43              : #include "lib/simplehash.h"
      44              : 
      45              : /*
      46              :  * SQL-callable function to create an advice stash
      47              :  */
      48              : Datum
      49            7 : pg_create_advice_stash(PG_FUNCTION_ARGS)
      50              : {
      51            7 :     char       *stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
      52              : 
      53            7 :     pgsa_check_stash_name(stash_name);
      54            3 :     if (unlikely(pgsa_entry_dshash == NULL))
      55            1 :         pgsa_attach();
      56            3 :     LWLockAcquire(&pgsa_state->lock, LW_EXCLUSIVE);
      57            3 :     pgsa_create_stash(stash_name);
      58            2 :     LWLockRelease(&pgsa_state->lock);
      59            2 :     PG_RETURN_VOID();
      60              : }
      61              : 
      62              : /*
      63              :  * SQL-callable function to drop an advice stash
      64              :  */
      65              : Datum
      66            3 : pg_drop_advice_stash(PG_FUNCTION_ARGS)
      67              : {
      68            3 :     char       *stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
      69              : 
      70            3 :     pgsa_check_stash_name(stash_name);
      71            3 :     if (unlikely(pgsa_entry_dshash == NULL))
      72            0 :         pgsa_attach();
      73            3 :     LWLockAcquire(&pgsa_state->lock, LW_EXCLUSIVE);
      74            3 :     pgsa_drop_stash(stash_name);
      75            2 :     LWLockRelease(&pgsa_state->lock);
      76            2 :     PG_RETURN_VOID();
      77              : }
      78              : 
      79              : /*
      80              :  * SQL-callable function to provide a list of advice stashes
      81              :  */
      82              : Datum
      83            2 : pg_get_advice_stashes(PG_FUNCTION_ARGS)
      84              : {
      85            2 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
      86              :     dshash_seq_status iterator;
      87              :     pgsa_entry *entry;
      88              :     pgsa_stash *stash;
      89              :     pgsa_stash_count_table_hash *chash;
      90              : 
      91            2 :     InitMaterializedSRF(fcinfo, 0);
      92              : 
      93              :     /* Attach to dynamic shared memory if not already done. */
      94            2 :     if (unlikely(pgsa_entry_dshash == NULL))
      95            0 :         pgsa_attach();
      96              : 
      97              :     /* Tally up the number of entries per stash. */
      98            2 :     chash = pgsa_stash_count_table_create(CurrentMemoryContext, 64, NULL);
      99            2 :     dshash_seq_init(&iterator, pgsa_entry_dshash, true);
     100            5 :     while ((entry = dshash_seq_next(&iterator)) != NULL)
     101              :     {
     102              :         pgsa_stash_count *c;
     103              :         bool        found;
     104              : 
     105            3 :         c = pgsa_stash_count_table_insert(chash,
     106              :                                           entry->key.pgsa_stash_id,
     107              :                                           &found);
     108            3 :         if (!found)
     109            2 :             c->num_entries = 1;
     110              :         else
     111            1 :             c->num_entries++;
     112              :     }
     113            2 :     dshash_seq_term(&iterator);
     114              : 
     115              :     /* Emit results. */
     116            2 :     dshash_seq_init(&iterator, pgsa_stash_dshash, true);
     117            6 :     while ((stash = dshash_seq_next(&iterator)) != NULL)
     118              :     {
     119              :         Datum       values[2];
     120              :         bool        nulls[2];
     121              :         pgsa_stash_count *c;
     122              : 
     123            4 :         values[0] = CStringGetTextDatum(stash->name);
     124            4 :         nulls[0] = false;
     125              : 
     126            4 :         c = pgsa_stash_count_table_lookup(chash, stash->pgsa_stash_id);
     127            4 :         values[1] = Int64GetDatum(c == NULL ? 0 : c->num_entries);
     128            4 :         nulls[1] = false;
     129              : 
     130            4 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
     131              :                              nulls);
     132              :     }
     133            2 :     dshash_seq_term(&iterator);
     134              : 
     135            2 :     return (Datum) 0;
     136              : }
     137              : 
     138              : /*
     139              :  * SQL-callable function to provide advice stash contents
     140              :  */
     141              : Datum
     142            5 : pg_get_advice_stash_contents(PG_FUNCTION_ARGS)
     143              : {
     144            5 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     145              :     dshash_seq_status iterator;
     146            5 :     char       *stash_name = NULL;
     147            5 :     pgsa_stash_name_table_hash *nhash = NULL;
     148            5 :     uint64      stash_id = 0;
     149              :     pgsa_entry *entry;
     150              : 
     151            5 :     InitMaterializedSRF(fcinfo, 0);
     152              : 
     153              :     /* Attach to dynamic shared memory if not already done. */
     154            5 :     if (unlikely(pgsa_entry_dshash == NULL))
     155            0 :         pgsa_attach();
     156              : 
     157              :     /* User can pass NULL for all stashes, or the name of a specific stash. */
     158            5 :     if (!PG_ARGISNULL(0))
     159              :     {
     160            4 :         stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     161            4 :         pgsa_check_stash_name(stash_name);
     162            4 :         stash_id = pgsa_lookup_stash_id(stash_name);
     163              : 
     164              :         /* If the user specified a stash name, it should exist. */
     165            4 :         if (stash_id == 0)
     166            1 :             ereport(ERROR,
     167              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     168              :                     errmsg("advice stash \"%s\" does not exist", stash_name));
     169              :     }
     170              :     else
     171              :     {
     172              :         pgsa_stash *stash;
     173              : 
     174              :         /*
     175              :          * If we're dumping data about all stashes, we need an ID->name lookup
     176              :          * table.
     177              :          */
     178            1 :         nhash = pgsa_stash_name_table_create(CurrentMemoryContext, 64, NULL);
     179            1 :         dshash_seq_init(&iterator, pgsa_stash_dshash, true);
     180            3 :         while ((stash = dshash_seq_next(&iterator)) != NULL)
     181              :         {
     182              :             pgsa_stash_name *n;
     183              :             bool        found;
     184              : 
     185            2 :             n = pgsa_stash_name_table_insert(nhash,
     186              :                                              stash->pgsa_stash_id,
     187              :                                              &found);
     188              :             Assert(!found);
     189            2 :             n->name = pstrdup(stash->name);
     190              :         }
     191            1 :         dshash_seq_term(&iterator);
     192              :     }
     193              : 
     194              :     /* Now iterate over all the entries. */
     195            4 :     dshash_seq_init(&iterator, pgsa_entry_dshash, false);
     196           11 :     while ((entry = dshash_seq_next(&iterator)) != NULL)
     197              :     {
     198              :         Datum       values[3];
     199              :         bool        nulls[3];
     200              :         char       *this_stash_name;
     201              :         char       *advice_string;
     202              : 
     203              :         /* Skip incomplete entries where the advice string was never set. */
     204            7 :         if (entry->advice_string == InvalidDsaPointer)
     205            2 :             continue;
     206              : 
     207            7 :         if (stash_id != 0)
     208              :         {
     209              :             /*
     210              :              * We're only dumping data for one particular stash, so skip
     211              :              * entries for any other stash and use the stash name specified by
     212              :              * the user.
     213              :              */
     214            5 :             if (stash_id != entry->key.pgsa_stash_id)
     215            2 :                 continue;
     216            3 :             this_stash_name = stash_name;
     217              :         }
     218              :         else
     219              :         {
     220              :             pgsa_stash_name *n;
     221              : 
     222              :             /*
     223              :              * We're dumping data for all stashes, so look up the correct name
     224              :              * to use in the hash table. If nothing is found, which is
     225              :              * possible due to race conditions, make up a string to use.
     226              :              */
     227            2 :             n = pgsa_stash_name_table_lookup(nhash, entry->key.pgsa_stash_id);
     228            2 :             if (n != NULL)
     229            2 :                 this_stash_name = n->name;
     230              :             else
     231            0 :                 this_stash_name = psprintf("<stash %" PRIu64 ">",
     232              :                                            entry->key.pgsa_stash_id);
     233              :         }
     234              : 
     235              :         /* Work out tuple values. */
     236            5 :         values[0] = CStringGetTextDatum(this_stash_name);
     237            5 :         nulls[0] = false;
     238            5 :         values[1] = Int64GetDatum(entry->key.queryId);
     239            5 :         nulls[1] = false;
     240            5 :         advice_string = dsa_get_address(pgsa_dsa_area, entry->advice_string);
     241            5 :         values[2] = CStringGetTextDatum(advice_string);
     242            5 :         nulls[2] = false;
     243              : 
     244              :         /* Emit the tuple. */
     245            5 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values,
     246              :                              nulls);
     247              :     }
     248            4 :     dshash_seq_term(&iterator);
     249              : 
     250            4 :     return (Datum) 0;
     251              : }
     252              : 
     253              : /*
     254              :  * SQL-callable function to update an advice stash entry for a particular
     255              :  * query ID
     256              :  *
     257              :  * If the second argument is NULL, we delete any existing advice stash
     258              :  * entry; otherwise, we either create an entry or update it with the new
     259              :  * advice string.
     260              :  */
     261              : Datum
     262            8 : pg_set_stashed_advice(PG_FUNCTION_ARGS)
     263              : {
     264              :     char       *stash_name;
     265              :     int64       queryId;
     266              : 
     267            8 :     if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
     268            0 :         PG_RETURN_NULL();
     269              : 
     270              :     /* Get and check advice stash name. */
     271            8 :     stash_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
     272            8 :     pgsa_check_stash_name(stash_name);
     273              : 
     274              :     /*
     275              :      * Get and check query ID.
     276              :      *
     277              :      * queryID 0 means no query ID was computed, so reject that.
     278              :      */
     279            8 :     queryId = PG_GETARG_INT64(1);
     280            8 :     if (queryId == 0)
     281            1 :         ereport(ERROR,
     282              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     283              :                 errmsg("cannot set advice string for query ID 0"));
     284              : 
     285              :     /* Attach to dynamic shared memory if not already done. */
     286            7 :     if (unlikely(pgsa_entry_dshash == NULL))
     287            0 :         pgsa_attach();
     288              : 
     289              :     /* Now call the appropriate function to do the real work. */
     290            7 :     if (PG_ARGISNULL(2))
     291              :     {
     292            2 :         LWLockAcquire(&pgsa_state->lock, LW_SHARED);
     293            2 :         pgsa_clear_advice_string(stash_name, queryId);
     294            1 :         LWLockRelease(&pgsa_state->lock);
     295              :     }
     296              :     else
     297              :     {
     298            5 :         char       *advice_string = text_to_cstring(PG_GETARG_TEXT_PP(2));
     299              : 
     300            5 :         LWLockAcquire(&pgsa_state->lock, LW_SHARED);
     301            5 :         pgsa_set_advice_string(stash_name, queryId, advice_string);
     302            4 :         LWLockRelease(&pgsa_state->lock);
     303              :     }
     304              : 
     305            5 :     PG_RETURN_VOID();
     306              : }
        

Generated by: LCOV version 2.0-1