LCOV - code coverage report
Current view: top level - src/backend/utils/cache - funccache.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 147 164 89.6 %
Date: 2025-04-30 00:16:16 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * funccache.c
       4             :  *    Function cache management.
       5             :  *
       6             :  * funccache.c manages a cache of function execution data.  The cache
       7             :  * is used by SQL-language and PL/pgSQL functions, and could be used by
       8             :  * other function languages.  Each cache entry is specific to the execution
       9             :  * of a particular function (identified by OID) with specific input data
      10             :  * types; so a polymorphic function could have many associated cache entries.
      11             :  * Trigger functions similarly have a cache entry per trigger.  These rules
      12             :  * allow the cached data to be specific to the particular data types the
      13             :  * function call will be dealing with.
      14             :  *
      15             :  *
      16             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
      17             :  * Portions Copyright (c) 1994, Regents of the University of California
      18             :  *
      19             :  * IDENTIFICATION
      20             :  *    src/backend/utils/cache/funccache.c
      21             :  *
      22             :  *-------------------------------------------------------------------------
      23             :  */
      24             : #include "postgres.h"
      25             : 
      26             : #include "catalog/pg_proc.h"
      27             : #include "commands/event_trigger.h"
      28             : #include "commands/trigger.h"
      29             : #include "common/hashfn.h"
      30             : #include "funcapi.h"
      31             : #include "utils/funccache.h"
      32             : #include "utils/hsearch.h"
      33             : #include "utils/syscache.h"
      34             : 
      35             : 
      36             : /*
      37             :  * Hash table for cached functions
      38             :  */
      39             : static HTAB *cfunc_hashtable = NULL;
      40             : 
      41             : typedef struct CachedFunctionHashEntry
      42             : {
      43             :     CachedFunctionHashKey key;  /* hash key, must be first */
      44             :     CachedFunction *function;   /* points to data of language-specific size */
      45             : } CachedFunctionHashEntry;
      46             : 
      47             : #define FUNCS_PER_USER      128 /* initial table size */
      48             : 
      49             : static uint32 cfunc_hash(const void *key, Size keysize);
      50             : static int  cfunc_match(const void *key1, const void *key2, Size keysize);
      51             : 
      52             : 
      53             : /*
      54             :  * Initialize the hash table on first use.
      55             :  *
      56             :  * The hash table will be in TopMemoryContext regardless of caller's context.
      57             :  */
      58             : static void
      59        1744 : cfunc_hashtable_init(void)
      60             : {
      61             :     HASHCTL     ctl;
      62             : 
      63             :     /* don't allow double-initialization */
      64             :     Assert(cfunc_hashtable == NULL);
      65             : 
      66        1744 :     ctl.keysize = sizeof(CachedFunctionHashKey);
      67        1744 :     ctl.entrysize = sizeof(CachedFunctionHashEntry);
      68        1744 :     ctl.hash = cfunc_hash;
      69        1744 :     ctl.match = cfunc_match;
      70        1744 :     cfunc_hashtable = hash_create("Cached function hash",
      71             :                                   FUNCS_PER_USER,
      72             :                                   &ctl,
      73             :                                   HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
      74        1744 : }
      75             : 
      76             : /*
      77             :  * cfunc_hash: hash function for cfunc hash table
      78             :  *
      79             :  * We need special hash and match functions to deal with the optional
      80             :  * presence of a TupleDesc in the hash keys.  As long as we have to do
      81             :  * that, we might as well also be smart about not comparing unused
      82             :  * elements of the argtypes arrays.
      83             :  */
      84             : static uint32
      85       81110 : cfunc_hash(const void *key, Size keysize)
      86             : {
      87       81110 :     const CachedFunctionHashKey *k = (const CachedFunctionHashKey *) key;
      88             :     uint32      h;
      89             : 
      90             :     Assert(keysize == sizeof(CachedFunctionHashKey));
      91             :     /* Hash all the fixed fields except callResultType */
      92       81110 :     h = DatumGetUInt32(hash_any((const unsigned char *) k,
      93             :                                 offsetof(CachedFunctionHashKey, callResultType)));
      94             :     /* Incorporate input argument types */
      95       81110 :     if (k->nargs > 0)
      96       47784 :         h = hash_combine(h,
      97       47784 :                          DatumGetUInt32(hash_any((const unsigned char *) k->argtypes,
      98       47784 :                                                  k->nargs * sizeof(Oid))));
      99             :     /* Incorporate callResultType if present */
     100       81110 :     if (k->callResultType)
     101        1796 :         h = hash_combine(h, hashRowType(k->callResultType));
     102       81110 :     return h;
     103             : }
     104             : 
     105             : /*
     106             :  * cfunc_match: match function to use with cfunc_hash
     107             :  */
     108             : static int
     109       63362 : cfunc_match(const void *key1, const void *key2, Size keysize)
     110             : {
     111       63362 :     const CachedFunctionHashKey *k1 = (const CachedFunctionHashKey *) key1;
     112       63362 :     const CachedFunctionHashKey *k2 = (const CachedFunctionHashKey *) key2;
     113             : 
     114             :     Assert(keysize == sizeof(CachedFunctionHashKey));
     115             :     /* Compare all the fixed fields except callResultType */
     116       63362 :     if (memcmp(k1, k2, offsetof(CachedFunctionHashKey, callResultType)) != 0)
     117           0 :         return 1;               /* not equal */
     118             :     /* Compare input argument types (we just verified that nargs matches) */
     119       63362 :     if (k1->nargs > 0 &&
     120       40642 :         memcmp(k1->argtypes, k2->argtypes, k1->nargs * sizeof(Oid)) != 0)
     121           0 :         return 1;               /* not equal */
     122             :     /* Compare callResultType */
     123       63362 :     if (k1->callResultType)
     124             :     {
     125         826 :         if (k2->callResultType)
     126             :         {
     127         826 :             if (!equalRowTypes(k1->callResultType, k2->callResultType))
     128           0 :                 return 1;       /* not equal */
     129             :         }
     130             :         else
     131           0 :             return 1;           /* not equal */
     132             :     }
     133             :     else
     134             :     {
     135       62536 :         if (k2->callResultType)
     136           0 :             return 1;           /* not equal */
     137             :     }
     138       63362 :     return 0;                   /* equal */
     139             : }
     140             : 
     141             : /*
     142             :  * Look up the CachedFunction for the given hash key.
     143             :  * Returns NULL if not present.
     144             :  */
     145             : static CachedFunction *
     146       72020 : cfunc_hashtable_lookup(CachedFunctionHashKey *func_key)
     147             : {
     148             :     CachedFunctionHashEntry *hentry;
     149             : 
     150       72020 :     if (cfunc_hashtable == NULL)
     151        1744 :         return NULL;
     152             : 
     153       70276 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     154             :                                                      func_key,
     155             :                                                      HASH_FIND,
     156             :                                                      NULL);
     157       70276 :     if (hentry)
     158       62594 :         return hentry->function;
     159             :     else
     160        7682 :         return NULL;
     161             : }
     162             : 
     163             : /*
     164             :  * Insert a hash table entry.
     165             :  */
     166             : static void
     167       10066 : cfunc_hashtable_insert(CachedFunction *function,
     168             :                        CachedFunctionHashKey *func_key)
     169             : {
     170             :     CachedFunctionHashEntry *hentry;
     171             :     bool        found;
     172             : 
     173       10066 :     if (cfunc_hashtable == NULL)
     174        1744 :         cfunc_hashtable_init();
     175             : 
     176       10066 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     177             :                                                      func_key,
     178             :                                                      HASH_ENTER,
     179             :                                                      &found);
     180       10066 :     if (found)
     181           0 :         elog(WARNING, "trying to insert a function that already exists");
     182             : 
     183             :     /*
     184             :      * If there's a callResultType, copy it into TopMemoryContext.  If we're
     185             :      * unlucky enough for that to fail, leave the entry with null
     186             :      * callResultType, which will probably never match anything.
     187             :      */
     188       10066 :     if (func_key->callResultType)
     189             :     {
     190         530 :         MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     191             : 
     192         530 :         hentry->key.callResultType = NULL;
     193         530 :         hentry->key.callResultType = CreateTupleDescCopy(func_key->callResultType);
     194         530 :         MemoryContextSwitchTo(oldcontext);
     195             :     }
     196             : 
     197       10066 :     hentry->function = function;
     198             : 
     199             :     /* Set back-link from function to hashtable key */
     200       10066 :     function->fn_hashkey = &hentry->key;
     201       10066 : }
     202             : 
     203             : /*
     204             :  * Delete a hash table entry.
     205             :  */
     206             : static void
     207         768 : cfunc_hashtable_delete(CachedFunction *function)
     208             : {
     209             :     CachedFunctionHashEntry *hentry;
     210             :     TupleDesc   tupdesc;
     211             : 
     212             :     /* do nothing if not in table */
     213         768 :     if (function->fn_hashkey == NULL)
     214           0 :         return;
     215             : 
     216             :     /*
     217             :      * We need to free the callResultType if present, which is slightly tricky
     218             :      * because it has to be valid during the hashtable search.  Fortunately,
     219             :      * because we have the hashkey back-link, we can grab that pointer before
     220             :      * deleting the hashtable entry.
     221             :      */
     222         768 :     tupdesc = function->fn_hashkey->callResultType;
     223             : 
     224         768 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     225         768 :                                                      function->fn_hashkey,
     226             :                                                      HASH_REMOVE,
     227             :                                                      NULL);
     228         768 :     if (hentry == NULL)
     229           0 :         elog(WARNING, "trying to delete function that does not exist");
     230             : 
     231             :     /* Remove back link, which no longer points to allocated storage */
     232         768 :     function->fn_hashkey = NULL;
     233             : 
     234             :     /* Release the callResultType if present */
     235         768 :     if (tupdesc)
     236          30 :         FreeTupleDesc(tupdesc);
     237             : }
     238             : 
     239             : /*
     240             :  * Compute the hashkey for a given function invocation
     241             :  *
     242             :  * The hashkey is returned into the caller-provided storage at *hashkey.
     243             :  * Note however that if a callResultType is incorporated, we've not done
     244             :  * anything about copying that.
     245             :  */
     246             : static void
     247       72020 : compute_function_hashkey(FunctionCallInfo fcinfo,
     248             :                          Form_pg_proc procStruct,
     249             :                          CachedFunctionHashKey *hashkey,
     250             :                          Size cacheEntrySize,
     251             :                          bool includeResultType,
     252             :                          bool forValidator)
     253             : {
     254             :     /* Make sure pad bytes within fixed part of the struct are zero */
     255       72020 :     memset(hashkey, 0, offsetof(CachedFunctionHashKey, argtypes));
     256             : 
     257             :     /* get function OID */
     258       72020 :     hashkey->funcOid = fcinfo->flinfo->fn_oid;
     259             : 
     260             :     /* get call context */
     261       72020 :     hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
     262       72020 :     hashkey->isEventTrigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
     263             : 
     264             :     /* record cacheEntrySize so multiple languages can share hash table */
     265       72020 :     hashkey->cacheEntrySize = cacheEntrySize;
     266             : 
     267             :     /*
     268             :      * If DML trigger, include trigger's OID in the hash, so that each trigger
     269             :      * usage gets a different hash entry, allowing for e.g. different relation
     270             :      * rowtypes or transition table names.  In validation mode we do not know
     271             :      * what relation or transition table names are intended to be used, so we
     272             :      * leave trigOid zero; the hash entry built in this case will never be
     273             :      * used for any actual calls.
     274             :      *
     275             :      * We don't currently need to distinguish different event trigger usages
     276             :      * in the same way, since the special parameter variables don't vary in
     277             :      * type in that case.
     278             :      */
     279       72020 :     if (hashkey->isTrigger && !forValidator)
     280             :     {
     281       12938 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
     282             : 
     283       12938 :         hashkey->trigOid = trigdata->tg_trigger->tgoid;
     284             :     }
     285             : 
     286             :     /* get input collation, if known */
     287       72020 :     hashkey->inputCollation = fcinfo->fncollation;
     288             : 
     289             :     /*
     290             :      * We include only input arguments in the hash key, since output argument
     291             :      * types can be deduced from those, and it would require extra cycles to
     292             :      * include the output arguments.  But we have to resolve any polymorphic
     293             :      * argument types to the real types for the call.
     294             :      */
     295       72020 :     if (procStruct->pronargs > 0)
     296             :     {
     297       44394 :         hashkey->nargs = procStruct->pronargs;
     298       44394 :         memcpy(hashkey->argtypes, procStruct->proargtypes.values,
     299       44394 :                procStruct->pronargs * sizeof(Oid));
     300       44394 :         cfunc_resolve_polymorphic_argtypes(procStruct->pronargs,
     301       44394 :                                            hashkey->argtypes,
     302             :                                            NULL,    /* all args are inputs */
     303       44394 :                                            fcinfo->flinfo->fn_expr,
     304             :                                            forValidator,
     305       44394 :                                            NameStr(procStruct->proname));
     306             :     }
     307             : 
     308             :     /*
     309             :      * While regular OUT arguments are sufficiently represented by the
     310             :      * resolved input arguments, a function returning composite has additional
     311             :      * variability: ALTER TABLE/ALTER TYPE could affect what it returns. Also,
     312             :      * a function returning RECORD may depend on a column definition list to
     313             :      * determine its output rowtype.  If the caller needs the exact result
     314             :      * type to be part of the hash lookup key, we must run
     315             :      * get_call_result_type() to find that out.
     316             :      */
     317       72020 :     if (includeResultType)
     318             :     {
     319             :         Oid         resultTypeId;
     320             :         TupleDesc   tupdesc;
     321             : 
     322       37554 :         switch (get_call_result_type(fcinfo, &resultTypeId, &tupdesc))
     323             :         {
     324        1296 :             case TYPEFUNC_COMPOSITE:
     325             :             case TYPEFUNC_COMPOSITE_DOMAIN:
     326        1296 :                 hashkey->callResultType = tupdesc;
     327        1296 :                 break;
     328       36258 :             default:
     329             :                 /* scalar result, or indeterminate rowtype */
     330       36258 :                 break;
     331             :         }
     332             :     }
     333       72020 : }
     334             : 
     335             : /*
     336             :  * This is the same as the standard resolve_polymorphic_argtypes() function,
     337             :  * except that:
     338             :  * 1. We go ahead and report the error if we can't resolve the types.
     339             :  * 2. We treat RECORD-type input arguments (not output arguments) as if
     340             :  *    they were polymorphic, replacing their types with the actual input
     341             :  *    types if we can determine those.  This allows us to create a separate
     342             :  *    function cache entry for each named composite type passed to such an
     343             :  *    argument.
     344             :  * 3. In validation mode, we have no inputs to look at, so assume that
     345             :  *    polymorphic arguments are integer, integer-array or integer-range.
     346             :  */
     347             : void
     348       48264 : cfunc_resolve_polymorphic_argtypes(int numargs,
     349             :                                    Oid *argtypes, char *argmodes,
     350             :                                    Node *call_expr, bool forValidator,
     351             :                                    const char *proname)
     352             : {
     353             :     int         i;
     354             : 
     355       48264 :     if (!forValidator)
     356             :     {
     357             :         int         inargno;
     358             : 
     359             :         /* normal case, pass to standard routine */
     360       43736 :         if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
     361             :                                           call_expr))
     362           0 :             ereport(ERROR,
     363             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     364             :                      errmsg("could not determine actual argument "
     365             :                             "type for polymorphic function \"%s\"",
     366             :                             proname)));
     367             :         /* also, treat RECORD inputs (but not outputs) as polymorphic */
     368       43736 :         inargno = 0;
     369      122946 :         for (i = 0; i < numargs; i++)
     370             :         {
     371       79210 :             char        argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
     372             : 
     373       79210 :             if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
     374         132 :                 continue;
     375       79078 :             if (argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID)
     376             :             {
     377          26 :                 Oid         resolvedtype = get_call_expr_argtype(call_expr,
     378             :                                                                  inargno);
     379             : 
     380          26 :                 if (OidIsValid(resolvedtype))
     381          26 :                     argtypes[i] = resolvedtype;
     382             :             }
     383       79078 :             inargno++;
     384             :         }
     385             :     }
     386             :     else
     387             :     {
     388             :         /* special validation case (no need to do anything for RECORD) */
     389       10142 :         for (i = 0; i < numargs; i++)
     390             :         {
     391        5614 :             switch (argtypes[i])
     392             :             {
     393         890 :                 case ANYELEMENTOID:
     394             :                 case ANYNONARRAYOID:
     395             :                 case ANYENUMOID:    /* XXX dubious */
     396             :                 case ANYCOMPATIBLEOID:
     397             :                 case ANYCOMPATIBLENONARRAYOID:
     398         890 :                     argtypes[i] = INT4OID;
     399         890 :                     break;
     400         206 :                 case ANYARRAYOID:
     401             :                 case ANYCOMPATIBLEARRAYOID:
     402         206 :                     argtypes[i] = INT4ARRAYOID;
     403         206 :                     break;
     404          60 :                 case ANYRANGEOID:
     405             :                 case ANYCOMPATIBLERANGEOID:
     406          60 :                     argtypes[i] = INT4RANGEOID;
     407          60 :                     break;
     408           0 :                 case ANYMULTIRANGEOID:
     409           0 :                     argtypes[i] = INT4MULTIRANGEOID;
     410           0 :                     break;
     411        4458 :                 default:
     412        4458 :                     break;
     413             :             }
     414             :         }
     415             :     }
     416       48264 : }
     417             : 
     418             : /*
     419             :  * delete_function - clean up as much as possible of a stale function cache
     420             :  *
     421             :  * We can't release the CachedFunction struct itself, because of the
     422             :  * possibility that there are fn_extra pointers to it.  We can release
     423             :  * the subsidiary storage, but only if there are no active evaluations
     424             :  * in progress.  Otherwise we'll just leak that storage.  Since the
     425             :  * case would only occur if a pg_proc update is detected during a nested
     426             :  * recursive call on the function, a leak seems acceptable.
     427             :  *
     428             :  * Note that this can be called more than once if there are multiple fn_extra
     429             :  * pointers to the same function cache.  Hence be careful not to do things
     430             :  * twice.
     431             :  */
     432             : static void
     433         768 : delete_function(CachedFunction *func)
     434             : {
     435             :     /* remove function from hash table (might be done already) */
     436         768 :     cfunc_hashtable_delete(func);
     437             : 
     438             :     /* release the function's storage if safe and not done already */
     439         768 :     if (func->use_count == 0 &&
     440         768 :         func->dcallback != NULL)
     441             :     {
     442         768 :         func->dcallback(func);
     443         768 :         func->dcallback = NULL;
     444             :     }
     445         768 : }
     446             : 
     447             : /*
     448             :  * Compile a cached function, if no existing cache entry is suitable.
     449             :  *
     450             :  * fcinfo is the current call information.
     451             :  *
     452             :  * function should be NULL or the result of a previous call of
     453             :  * cached_function_compile() for the same fcinfo.  The caller will
     454             :  * typically save the result in fcinfo->flinfo->fn_extra, or in a
     455             :  * field of a struct pointed to by fn_extra, to re-use in later
     456             :  * calls within the same query.
     457             :  *
     458             :  * ccallback and dcallback are function-language-specific callbacks to
     459             :  * compile and delete a cached function entry.  dcallback can be NULL
     460             :  * if there's nothing for it to do.
     461             :  *
     462             :  * cacheEntrySize is the function-language-specific size of the cache entry
     463             :  * (which embeds a CachedFunction struct and typically has many more fields
     464             :  * after that).
     465             :  *
     466             :  * If includeResultType is true and the function returns composite,
     467             :  * include the actual result descriptor in the cache lookup key.
     468             :  *
     469             :  * If forValidator is true, we're only compiling for validation purposes,
     470             :  * and so some checks are skipped.
     471             :  *
     472             :  * Note: it's important for this to fall through quickly if the function
     473             :  * has already been compiled.
     474             :  *
     475             :  * Note: this function leaves the "use_count" field as zero.  The caller
     476             :  * is expected to increment the use_count and decrement it when done with
     477             :  * the cache entry.
     478             :  */
     479             : CachedFunction *
     480      182028 : cached_function_compile(FunctionCallInfo fcinfo,
     481             :                         CachedFunction *function,
     482             :                         CachedFunctionCompileCallback ccallback,
     483             :                         CachedFunctionDeleteCallback dcallback,
     484             :                         Size cacheEntrySize,
     485             :                         bool includeResultType,
     486             :                         bool forValidator)
     487             : {
     488      182028 :     Oid         funcOid = fcinfo->flinfo->fn_oid;
     489             :     HeapTuple   procTup;
     490             :     Form_pg_proc procStruct;
     491             :     CachedFunctionHashKey hashkey;
     492      182028 :     bool        function_valid = false;
     493      182028 :     bool        hashkey_valid = false;
     494             : 
     495             :     /*
     496             :      * Lookup the pg_proc tuple by Oid; we'll need it in any case
     497             :      */
     498      182028 :     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
     499      182028 :     if (!HeapTupleIsValid(procTup))
     500           0 :         elog(ERROR, "cache lookup failed for function %u", funcOid);
     501      182028 :     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
     502             : 
     503             :     /*
     504             :      * Do we already have a cache entry for the current FmgrInfo?  If not, try
     505             :      * to find one in the hash table.
     506             :      */
     507      182028 : recheck:
     508      182028 :     if (!function)
     509             :     {
     510             :         /* Compute hashkey using function signature and actual arg types */
     511       72020 :         compute_function_hashkey(fcinfo, procStruct, &hashkey,
     512             :                                  cacheEntrySize, includeResultType,
     513             :                                  forValidator);
     514       72020 :         hashkey_valid = true;
     515             : 
     516             :         /* And do the lookup */
     517       72020 :         function = cfunc_hashtable_lookup(&hashkey);
     518             :     }
     519             : 
     520      182028 :     if (function)
     521             :     {
     522             :         /* We have a compiled function, but is it still valid? */
     523      344442 :         if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
     524      171840 :             ItemPointerEquals(&function->fn_tid, &procTup->t_self))
     525      171834 :             function_valid = true;
     526             :         else
     527             :         {
     528             :             /*
     529             :              * Nope, so remove it from hashtable and try to drop associated
     530             :              * storage (if not done already).
     531             :              */
     532         768 :             delete_function(function);
     533             : 
     534             :             /*
     535             :              * If the function isn't in active use then we can overwrite the
     536             :              * func struct with new data, allowing any other existing fn_extra
     537             :              * pointers to make use of the new definition on their next use.
     538             :              * If it is in use then just leave it alone and make a new one.
     539             :              * (The active invocations will run to completion using the
     540             :              * previous definition, and then the cache entry will just be
     541             :              * leaked; doesn't seem worth adding code to clean it up, given
     542             :              * what a corner case this is.)
     543             :              *
     544             :              * If we found the function struct via fn_extra then it's possible
     545             :              * a replacement has already been made, so go back and recheck the
     546             :              * hashtable.
     547             :              */
     548         768 :             if (function->use_count != 0)
     549             :             {
     550           0 :                 function = NULL;
     551           0 :                 if (!hashkey_valid)
     552           0 :                     goto recheck;
     553             :             }
     554             :         }
     555             :     }
     556             : 
     557             :     /*
     558             :      * If the function wasn't found or was out-of-date, we have to compile it.
     559             :      */
     560      182028 :     if (!function_valid)
     561             :     {
     562             :         /*
     563             :          * Calculate hashkey if we didn't already; we'll need it to store the
     564             :          * completed function.
     565             :          */
     566       10194 :         if (!hashkey_valid)
     567           0 :             compute_function_hashkey(fcinfo, procStruct, &hashkey,
     568             :                                      cacheEntrySize, includeResultType,
     569             :                                      forValidator);
     570             : 
     571             :         /*
     572             :          * Create the new function struct, if not done already.  The function
     573             :          * structs are never thrown away, so keep them in TopMemoryContext.
     574             :          */
     575             :         Assert(cacheEntrySize >= sizeof(CachedFunction));
     576       10194 :         if (function == NULL)
     577             :         {
     578             :             function = (CachedFunction *)
     579        9426 :                 MemoryContextAllocZero(TopMemoryContext, cacheEntrySize);
     580             :         }
     581             :         else
     582             :         {
     583             :             /* re-using a previously existing struct, so clear it out */
     584         768 :             memset(function, 0, cacheEntrySize);
     585             :         }
     586             : 
     587             :         /*
     588             :          * Fill in the CachedFunction part.  fn_hashkey and use_count remain
     589             :          * zeroes for now.
     590             :          */
     591       10194 :         function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
     592       10194 :         function->fn_tid = procTup->t_self;
     593       10194 :         function->dcallback = dcallback;
     594             : 
     595             :         /*
     596             :          * Do the hard, language-specific part.
     597             :          */
     598       10194 :         ccallback(fcinfo, procTup, &hashkey, function, forValidator);
     599             : 
     600             :         /*
     601             :          * Add the completed struct to the hash table.
     602             :          */
     603       10066 :         cfunc_hashtable_insert(function, &hashkey);
     604             :     }
     605             : 
     606      181900 :     ReleaseSysCache(procTup);
     607             : 
     608             :     /*
     609             :      * Finally return the compiled function
     610             :      */
     611      181900 :     return function;
     612             : }

Generated by: LCOV version 1.14