LCOV - code coverage report
Current view: top level - src/backend/utils/cache - funccache.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.1 % 172 155
Test Date: 2026-03-01 03:15:02 Functions: 100.0 % 10 10
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-2026, 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          949 : cfunc_hashtable_init(void)
      60              : {
      61              :     HASHCTL     ctl;
      62              : 
      63              :     /* don't allow double-initialization */
      64              :     Assert(cfunc_hashtable == NULL);
      65              : 
      66          949 :     ctl.keysize = sizeof(CachedFunctionHashKey);
      67          949 :     ctl.entrysize = sizeof(CachedFunctionHashEntry);
      68          949 :     ctl.hash = cfunc_hash;
      69          949 :     ctl.match = cfunc_match;
      70          949 :     cfunc_hashtable = hash_create("Cached function hash",
      71              :                                   FUNCS_PER_USER,
      72              :                                   &ctl,
      73              :                                   HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
      74          949 : }
      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        41604 : cfunc_hash(const void *key, Size keysize)
      86              : {
      87        41604 :     const CachedFunctionHashKey *k = (const CachedFunctionHashKey *) key;
      88              :     uint32      h;
      89              : 
      90              :     Assert(keysize == sizeof(CachedFunctionHashKey));
      91              :     /* Hash all the fixed fields except callResultType */
      92        41604 :     h = DatumGetUInt32(hash_any((const unsigned char *) k,
      93              :                                 offsetof(CachedFunctionHashKey, callResultType)));
      94              :     /* Incorporate input argument types */
      95        41604 :     if (k->nargs > 0)
      96        24684 :         h = hash_combine(h,
      97        24684 :                          DatumGetUInt32(hash_any((const unsigned char *) k->argtypes,
      98        24684 :                                                  k->nargs * sizeof(Oid))));
      99              :     /* Incorporate callResultType if present */
     100        41604 :     if (k->callResultType)
     101          898 :         h = hash_combine(h, hashRowType(k->callResultType));
     102        41604 :     return h;
     103              : }
     104              : 
     105              : /*
     106              :  * cfunc_match: match function to use with cfunc_hash
     107              :  */
     108              : static int
     109        32404 : cfunc_match(const void *key1, const void *key2, Size keysize)
     110              : {
     111        32404 :     const CachedFunctionHashKey *k1 = (const CachedFunctionHashKey *) key1;
     112        32404 :     const CachedFunctionHashKey *k2 = (const CachedFunctionHashKey *) key2;
     113              : 
     114              :     Assert(keysize == sizeof(CachedFunctionHashKey));
     115              :     /* Compare all the fixed fields except callResultType */
     116        32404 :     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        32404 :     if (k1->nargs > 0 &&
     120        20912 :         memcmp(k1->argtypes, k2->argtypes, k1->nargs * sizeof(Oid)) != 0)
     121            0 :         return 1;               /* not equal */
     122              :     /* Compare callResultType */
     123        32404 :     if (k1->callResultType)
     124              :     {
     125          413 :         if (k2->callResultType)
     126              :         {
     127          413 :             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        31991 :         if (k2->callResultType)
     136            0 :             return 1;           /* not equal */
     137              :     }
     138        32404 :     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        36930 : cfunc_hashtable_lookup(CachedFunctionHashKey *func_key)
     147              : {
     148              :     CachedFunctionHashEntry *hentry;
     149              : 
     150        36930 :     if (cfunc_hashtable == NULL)
     151          949 :         return NULL;
     152              : 
     153        35981 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     154              :                                                      func_key,
     155              :                                                      HASH_FIND,
     156              :                                                      NULL);
     157        35981 :     if (hentry)
     158        32017 :         return hentry->function;
     159              :     else
     160         3964 :         return NULL;
     161              : }
     162              : 
     163              : /*
     164              :  * Insert a hash table entry.
     165              :  */
     166              : static void
     167         5236 : cfunc_hashtable_insert(CachedFunction *function,
     168              :                        CachedFunctionHashKey *func_key)
     169              : {
     170              :     CachedFunctionHashEntry *hentry;
     171              :     bool        found;
     172              : 
     173         5236 :     if (cfunc_hashtable == NULL)
     174          949 :         cfunc_hashtable_init();
     175              : 
     176         5236 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     177              :                                                      func_key,
     178              :                                                      HASH_ENTER,
     179              :                                                      &found);
     180         5236 :     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         5236 :     if (func_key->callResultType)
     189              :     {
     190          265 :         MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
     191              : 
     192          265 :         hentry->key.callResultType = NULL;
     193          265 :         hentry->key.callResultType = CreateTupleDescCopy(func_key->callResultType);
     194          265 :         MemoryContextSwitchTo(oldcontext);
     195              :     }
     196              : 
     197         5236 :     hentry->function = function;
     198              : 
     199              :     /* Set back-link from function to hashtable key */
     200         5236 :     function->fn_hashkey = &hentry->key;
     201         5236 : }
     202              : 
     203              : /*
     204              :  * Delete a hash table entry.
     205              :  */
     206              : static void
     207          387 : cfunc_hashtable_delete(CachedFunction *function)
     208              : {
     209              :     CachedFunctionHashEntry *hentry;
     210              :     TupleDesc   tupdesc;
     211              : 
     212              :     /* do nothing if not in table */
     213          387 :     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          387 :     tupdesc = function->fn_hashkey->callResultType;
     223              : 
     224          387 :     hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
     225          387 :                                                      function->fn_hashkey,
     226              :                                                      HASH_REMOVE,
     227              :                                                      NULL);
     228          387 :     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          387 :     function->fn_hashkey = NULL;
     233              : 
     234              :     /* Release the callResultType if present */
     235          387 :     if (tupdesc)
     236           15 :         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        36930 : 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        36930 :     memset(hashkey, 0, offsetof(CachedFunctionHashKey, argtypes));
     256              : 
     257              :     /* get function OID */
     258        36930 :     hashkey->funcOid = fcinfo->flinfo->fn_oid;
     259              : 
     260              :     /* get call context */
     261        36930 :     hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
     262        36930 :     hashkey->isEventTrigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
     263              : 
     264              :     /* record cacheEntrySize so multiple languages can share hash table */
     265        36930 :     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        36930 :     if (hashkey->isTrigger && !forValidator)
     280              :     {
     281         6586 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
     282              : 
     283         6586 :         hashkey->trigOid = trigdata->tg_trigger->tgoid;
     284              :     }
     285              : 
     286              :     /* get input collation, if known */
     287        36930 :     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        36930 :     if (procStruct->pronargs > 0)
     296              :     {
     297        22915 :         hashkey->nargs = procStruct->pronargs;
     298        22915 :         memcpy(hashkey->argtypes, procStruct->proargtypes.values,
     299        22915 :                procStruct->pronargs * sizeof(Oid));
     300        22915 :         cfunc_resolve_polymorphic_argtypes(procStruct->pronargs,
     301        22915 :                                            hashkey->argtypes,
     302              :                                            NULL,    /* all args are inputs */
     303        22915 :                                            fcinfo->flinfo->fn_expr,
     304              :                                            forValidator,
     305        22915 :                                            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        36930 :     if (includeResultType)
     318              :     {
     319              :         Oid         resultTypeId;
     320              :         TupleDesc   tupdesc;
     321              : 
     322        19121 :         switch (get_call_result_type(fcinfo, &resultTypeId, &tupdesc))
     323              :         {
     324          648 :             case TYPEFUNC_COMPOSITE:
     325              :             case TYPEFUNC_COMPOSITE_DOMAIN:
     326          648 :                 hashkey->callResultType = tupdesc;
     327          648 :                 break;
     328        18473 :             default:
     329              :                 /* scalar result, or indeterminate rowtype */
     330        18473 :                 break;
     331              :         }
     332              :     }
     333        36930 : }
     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        24936 : 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        24936 :     if (!forValidator)
     356              :     {
     357              :         int         inargno;
     358              : 
     359              :         /* normal case, pass to standard routine */
     360        22587 :         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        22587 :         inargno = 0;
     369        63568 :         for (i = 0; i < numargs; i++)
     370              :         {
     371        40981 :             char        argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
     372              : 
     373        40981 :             if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
     374           66 :                 continue;
     375        40915 :             if (argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID)
     376              :             {
     377          271 :                 Oid         resolvedtype = get_call_expr_argtype(call_expr,
     378              :                                                                  inargno);
     379              : 
     380          271 :                 if (OidIsValid(resolvedtype))
     381          271 :                     argtypes[i] = resolvedtype;
     382              :             }
     383        40915 :             inargno++;
     384              :         }
     385              :     }
     386              :     else
     387              :     {
     388              :         /* special validation case (no need to do anything for RECORD) */
     389         5328 :         for (i = 0; i < numargs; i++)
     390              :         {
     391         2979 :             switch (argtypes[i])
     392              :             {
     393          473 :                 case ANYELEMENTOID:
     394              :                 case ANYNONARRAYOID:
     395              :                 case ANYENUMOID:    /* XXX dubious */
     396              :                 case ANYCOMPATIBLEOID:
     397              :                 case ANYCOMPATIBLENONARRAYOID:
     398          473 :                     argtypes[i] = INT4OID;
     399          473 :                     break;
     400          103 :                 case ANYARRAYOID:
     401              :                 case ANYCOMPATIBLEARRAYOID:
     402          103 :                     argtypes[i] = INT4ARRAYOID;
     403          103 :                     break;
     404           30 :                 case ANYRANGEOID:
     405              :                 case ANYCOMPATIBLERANGEOID:
     406           30 :                     argtypes[i] = INT4RANGEOID;
     407           30 :                     break;
     408            0 :                 case ANYMULTIRANGEOID:
     409            0 :                     argtypes[i] = INT4MULTIRANGEOID;
     410            0 :                     break;
     411         2373 :                 default:
     412         2373 :                     break;
     413              :             }
     414              :         }
     415              :     }
     416        24936 : }
     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          387 : delete_function(CachedFunction *func)
     434              : {
     435              :     /* remove function from hash table (might be done already) */
     436          387 :     cfunc_hashtable_delete(func);
     437              : 
     438              :     /* release the function's storage if safe and not done already */
     439          387 :     if (func->use_count == 0 &&
     440          387 :         func->dcallback != NULL)
     441              :     {
     442          387 :         func->dcallback(func);
     443          387 :         func->dcallback = NULL;
     444              :     }
     445          387 : }
     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        92689 : 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        92689 :     Oid         funcOid = fcinfo->flinfo->fn_oid;
     489              :     HeapTuple   procTup;
     490              :     Form_pg_proc procStruct;
     491              :     CachedFunctionHashKey hashkey;
     492        92689 :     bool        function_valid = false;
     493        92689 :     bool        hashkey_valid = false;
     494        92689 :     bool        new_function = false;
     495              : 
     496              :     /*
     497              :      * Lookup the pg_proc tuple by Oid; we'll need it in any case
     498              :      */
     499        92689 :     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
     500        92689 :     if (!HeapTupleIsValid(procTup))
     501            0 :         elog(ERROR, "cache lookup failed for function %u", funcOid);
     502        92689 :     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
     503              : 
     504              :     /*
     505              :      * Do we already have a cache entry for the current FmgrInfo?  If not, try
     506              :      * to find one in the hash table.
     507              :      */
     508        92689 : recheck:
     509        92689 :     if (!function)
     510              :     {
     511              :         /* Compute hashkey using function signature and actual arg types */
     512        36930 :         compute_function_hashkey(fcinfo, procStruct, &hashkey,
     513              :                                  cacheEntrySize, includeResultType,
     514              :                                  forValidator);
     515        36930 :         hashkey_valid = true;
     516              : 
     517              :         /* And do the lookup */
     518        36930 :         function = cfunc_hashtable_lookup(&hashkey);
     519              :     }
     520              : 
     521        92689 :     if (function)
     522              :     {
     523              :         /* We have a compiled function, but is it still valid? */
     524        87776 :         if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
     525        87392 :             ItemPointerEquals(&function->fn_tid, &procTup->t_self))
     526        87389 :             function_valid = true;
     527              :         else
     528              :         {
     529              :             /*
     530              :              * Nope, so remove it from hashtable and try to drop associated
     531              :              * storage (if not done already).
     532              :              */
     533          387 :             delete_function(function);
     534              : 
     535              :             /*
     536              :              * If the function isn't in active use then we can overwrite the
     537              :              * func struct with new data, allowing any other existing fn_extra
     538              :              * pointers to make use of the new definition on their next use.
     539              :              * If it is in use then just leave it alone and make a new one.
     540              :              * (The active invocations will run to completion using the
     541              :              * previous definition, and then the cache entry will just be
     542              :              * leaked; doesn't seem worth adding code to clean it up, given
     543              :              * what a corner case this is.)
     544              :              *
     545              :              * If we found the function struct via fn_extra then it's possible
     546              :              * a replacement has already been made, so go back and recheck the
     547              :              * hashtable.
     548              :              */
     549          387 :             if (function->use_count != 0)
     550              :             {
     551            0 :                 function = NULL;
     552            0 :                 if (!hashkey_valid)
     553            0 :                     goto recheck;
     554              :             }
     555              :         }
     556              :     }
     557              : 
     558              :     /*
     559              :      * If the function wasn't found or was out-of-date, we have to compile it.
     560              :      */
     561        92689 :     if (!function_valid)
     562              :     {
     563              :         /*
     564              :          * Calculate hashkey if we didn't already; we'll need it to store the
     565              :          * completed function.
     566              :          */
     567         5300 :         if (!hashkey_valid)
     568            0 :             compute_function_hashkey(fcinfo, procStruct, &hashkey,
     569              :                                      cacheEntrySize, includeResultType,
     570              :                                      forValidator);
     571              : 
     572              :         /*
     573              :          * Create the new function struct, if not done already.  The function
     574              :          * cache entry will be kept for the life of the backend, so put it in
     575              :          * TopMemoryContext.
     576              :          */
     577              :         Assert(cacheEntrySize >= sizeof(CachedFunction));
     578         5300 :         if (function == NULL)
     579              :         {
     580              :             function = (CachedFunction *)
     581         4913 :                 MemoryContextAllocZero(TopMemoryContext, cacheEntrySize);
     582         4913 :             new_function = true;
     583              :         }
     584              :         else
     585              :         {
     586              :             /* re-using a previously existing struct, so clear it out */
     587          387 :             memset(function, 0, cacheEntrySize);
     588              :         }
     589              : 
     590              :         /*
     591              :          * However, if function compilation fails, we'd like not to leak the
     592              :          * function struct, so use a PG_TRY block to prevent that.  (It's up
     593              :          * to the compile callback function to avoid its own internal leakage
     594              :          * in such cases.)  Unfortunately, freeing the struct is only safe if
     595              :          * we just allocated it: otherwise there are probably fn_extra
     596              :          * pointers to it.
     597              :          */
     598         5300 :         PG_TRY();
     599              :         {
     600              :             /*
     601              :              * Do the hard, language-specific part.
     602              :              */
     603         5300 :             ccallback(fcinfo, procTup, &hashkey, function, forValidator);
     604              :         }
     605           64 :         PG_CATCH();
     606              :         {
     607           64 :             if (new_function)
     608           64 :                 pfree(function);
     609           64 :             PG_RE_THROW();
     610              :         }
     611         5236 :         PG_END_TRY();
     612              : 
     613              :         /*
     614              :          * Fill in the CachedFunction part.  (We do this last to prevent the
     615              :          * function from looking valid before it's fully built.)  fn_hashkey
     616              :          * will be set by cfunc_hashtable_insert; use_count remains zero.
     617              :          */
     618         5236 :         function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
     619         5236 :         function->fn_tid = procTup->t_self;
     620         5236 :         function->dcallback = dcallback;
     621              : 
     622              :         /*
     623              :          * Add the completed struct to the hash table.
     624              :          */
     625         5236 :         cfunc_hashtable_insert(function, &hashkey);
     626              :     }
     627              : 
     628        92625 :     ReleaseSysCache(procTup);
     629              : 
     630              :     /*
     631              :      * Finally return the compiled function
     632              :      */
     633        92625 :     return function;
     634              : }
        

Generated by: LCOV version 2.0-1