Age Owner Branch data TLA 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
157 tgl@sss.pgh.pa.us 59 :CBC 901 : cfunc_hashtable_init(void)
60 : : {
61 : : HASHCTL ctl;
62 : :
63 : : /* don't allow double-initialization */
64 [ - + ]: 901 : Assert(cfunc_hashtable == NULL);
65 : :
66 : 901 : ctl.keysize = sizeof(CachedFunctionHashKey);
67 : 901 : ctl.entrysize = sizeof(CachedFunctionHashEntry);
68 : 901 : ctl.hash = cfunc_hash;
69 : 901 : ctl.match = cfunc_match;
70 : 901 : cfunc_hashtable = hash_create("Cached function hash",
71 : : FUNCS_PER_USER,
72 : : &ctl,
73 : : HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
74 : 901 : }
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 : 38779 : cfunc_hash(const void *key, Size keysize)
86 : : {
87 : 38779 : const CachedFunctionHashKey *k = (const CachedFunctionHashKey *) key;
88 : : uint32 h;
89 : :
90 [ - + ]: 38779 : Assert(keysize == sizeof(CachedFunctionHashKey));
91 : : /* Hash all the fixed fields except callResultType */
92 : 38779 : h = DatumGetUInt32(hash_any((const unsigned char *) k,
93 : : offsetof(CachedFunctionHashKey, callResultType)));
94 : : /* Incorporate input argument types */
95 [ + + ]: 38779 : if (k->nargs > 0)
96 : 21949 : h = hash_combine(h,
97 : 21949 : DatumGetUInt32(hash_any((const unsigned char *) k->argtypes,
98 : 21949 : k->nargs * sizeof(Oid))));
99 : : /* Incorporate callResultType if present */
100 [ + + ]: 38779 : if (k->callResultType)
101 : 898 : h = hash_combine(h, hashRowType(k->callResultType));
102 : 38779 : return h;
103 : : }
104 : :
105 : : /*
106 : : * cfunc_match: match function to use with cfunc_hash
107 : : */
108 : : static int
109 : 29928 : cfunc_match(const void *key1, const void *key2, Size keysize)
110 : : {
111 : 29928 : const CachedFunctionHashKey *k1 = (const CachedFunctionHashKey *) key1;
112 : 29928 : const CachedFunctionHashKey *k2 = (const CachedFunctionHashKey *) key2;
113 : :
114 [ - + ]: 29928 : Assert(keysize == sizeof(CachedFunctionHashKey));
115 : : /* Compare all the fixed fields except callResultType */
116 [ - + ]: 29928 : if (memcmp(k1, k2, offsetof(CachedFunctionHashKey, callResultType)) != 0)
157 tgl@sss.pgh.pa.us 117 :UBC 0 : return 1; /* not equal */
118 : : /* Compare input argument types (we just verified that nargs matches) */
157 tgl@sss.pgh.pa.us 119 [ + + ]:CBC 29928 : if (k1->nargs > 0 &&
120 [ - + ]: 18480 : memcmp(k1->argtypes, k2->argtypes, k1->nargs * sizeof(Oid)) != 0)
157 tgl@sss.pgh.pa.us 121 :UBC 0 : return 1; /* not equal */
122 : : /* Compare callResultType */
157 tgl@sss.pgh.pa.us 123 [ + + ]:CBC 29928 : if (k1->callResultType)
124 : : {
125 [ + - ]: 413 : if (k2->callResultType)
126 : : {
127 [ - + ]: 413 : if (!equalRowTypes(k1->callResultType, k2->callResultType))
157 tgl@sss.pgh.pa.us 128 :UBC 0 : return 1; /* not equal */
129 : : }
130 : : else
131 : 0 : return 1; /* not equal */
132 : : }
133 : : else
134 : : {
157 tgl@sss.pgh.pa.us 135 [ - + ]:CBC 29515 : if (k2->callResultType)
157 tgl@sss.pgh.pa.us 136 :UBC 0 : return 1; /* not equal */
137 : : }
157 tgl@sss.pgh.pa.us 138 :CBC 29928 : 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 : 34260 : cfunc_hashtable_lookup(CachedFunctionHashKey *func_key)
147 : : {
148 : : CachedFunctionHashEntry *hentry;
149 : :
150 [ + + ]: 34260 : if (cfunc_hashtable == NULL)
151 : 901 : return NULL;
152 : :
153 : 33359 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
154 : : func_key,
155 : : HASH_FIND,
156 : : NULL);
157 [ + + ]: 33359 : if (hentry)
158 : 29544 : return hentry->function;
159 : : else
160 : 3815 : return NULL;
161 : : }
162 : :
163 : : /*
164 : : * Insert a hash table entry.
165 : : */
166 : : static void
167 : 5036 : cfunc_hashtable_insert(CachedFunction *function,
168 : : CachedFunctionHashKey *func_key)
169 : : {
170 : : CachedFunctionHashEntry *hentry;
171 : : bool found;
172 : :
173 [ + + ]: 5036 : if (cfunc_hashtable == NULL)
174 : 901 : cfunc_hashtable_init();
175 : :
176 : 5036 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
177 : : func_key,
178 : : HASH_ENTER,
179 : : &found);
180 [ - + ]: 5036 : if (found)
157 tgl@sss.pgh.pa.us 181 [ # # ]:UBC 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 : : */
157 tgl@sss.pgh.pa.us 188 [ + + ]:CBC 5036 : 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 : 5036 : hentry->function = function;
198 : :
199 : : /* Set back-link from function to hashtable key */
200 : 5036 : function->fn_hashkey = &hentry->key;
201 : 5036 : }
202 : :
203 : : /*
204 : : * Delete a hash table entry.
205 : : */
206 : : static void
207 : 384 : cfunc_hashtable_delete(CachedFunction *function)
208 : : {
209 : : CachedFunctionHashEntry *hentry;
210 : : TupleDesc tupdesc;
211 : :
212 : : /* do nothing if not in table */
213 [ - + ]: 384 : if (function->fn_hashkey == NULL)
157 tgl@sss.pgh.pa.us 214 :UBC 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 : : */
157 tgl@sss.pgh.pa.us 222 :CBC 384 : tupdesc = function->fn_hashkey->callResultType;
223 : :
224 : 384 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
225 : 384 : function->fn_hashkey,
226 : : HASH_REMOVE,
227 : : NULL);
228 [ - + ]: 384 : if (hentry == NULL)
157 tgl@sss.pgh.pa.us 229 [ # # ]:UBC 0 : elog(WARNING, "trying to delete function that does not exist");
230 : :
231 : : /* Remove back link, which no longer points to allocated storage */
157 tgl@sss.pgh.pa.us 232 :CBC 384 : function->fn_hashkey = NULL;
233 : :
234 : : /* Release the callResultType if present */
235 [ + + ]: 384 : 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 : 34260 : 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 : 34260 : memset(hashkey, 0, offsetof(CachedFunctionHashKey, argtypes));
256 : :
257 : : /* get function OID */
258 : 34260 : hashkey->funcOid = fcinfo->flinfo->fn_oid;
259 : :
260 : : /* get call context */
261 [ + + + + ]: 34260 : hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
262 [ + + + + ]: 34260 : hashkey->isEventTrigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
263 : :
264 : : /* record cacheEntrySize so multiple languages can share hash table */
265 : 34260 : 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 [ + + + + ]: 34260 : if (hashkey->isTrigger && !forValidator)
280 : : {
281 : 6444 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
282 : :
283 : 6444 : hashkey->trigOid = trigdata->tg_trigger->tgoid;
284 : : }
285 : :
286 : : /* get input collation, if known */
287 : 34260 : 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 [ + + ]: 34260 : if (procStruct->pronargs > 0)
296 : : {
297 : 20278 : hashkey->nargs = procStruct->pronargs;
298 : 20278 : memcpy(hashkey->argtypes, procStruct->proargtypes.values,
299 : 20278 : procStruct->pronargs * sizeof(Oid));
300 : 20278 : cfunc_resolve_polymorphic_argtypes(procStruct->pronargs,
301 : 20278 : hashkey->argtypes,
302 : : NULL, /* all args are inputs */
303 : 20278 : fcinfo->flinfo->fn_expr,
304 : : forValidator,
305 : 20278 : 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 [ + + ]: 34260 : if (includeResultType)
318 : : {
319 : : Oid resultTypeId;
320 : : TupleDesc tupdesc;
321 : :
322 [ + + ]: 17918 : 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 : 17270 : default:
329 : : /* scalar result, or indeterminate rowtype */
330 : 17270 : break;
331 : : }
332 : : }
333 : 34260 : }
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 : 22263 : 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 [ + + ]: 22263 : if (!forValidator)
356 : : {
357 : : int inargno;
358 : :
359 : : /* normal case, pass to standard routine */
360 [ - + ]: 20019 : if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
361 : : call_expr))
157 tgl@sss.pgh.pa.us 362 [ # # ]:UBC 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 */
157 tgl@sss.pgh.pa.us 368 :CBC 20019 : inargno = 0;
369 [ + + ]: 55696 : for (i = 0; i < numargs; i++)
370 : : {
371 [ + + ]: 35677 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
372 : :
373 [ + + + + ]: 35677 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
374 : 66 : continue;
375 [ + + - + ]: 35611 : if (argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID)
376 : : {
377 : 13 : Oid resolvedtype = get_call_expr_argtype(call_expr,
378 : : inargno);
379 : :
380 [ + - ]: 13 : if (OidIsValid(resolvedtype))
381 : 13 : argtypes[i] = resolvedtype;
382 : : }
383 : 35611 : inargno++;
384 : : }
385 : : }
386 : : else
387 : : {
388 : : /* special validation case (no need to do anything for RECORD) */
389 [ + + ]: 4997 : for (i = 0; i < numargs; i++)
390 : : {
391 [ + + + - : 2753 : switch (argtypes[i])
+ ]
392 : : {
393 : 445 : case ANYELEMENTOID:
394 : : case ANYNONARRAYOID:
395 : : case ANYENUMOID: /* XXX dubious */
396 : : case ANYCOMPATIBLEOID:
397 : : case ANYCOMPATIBLENONARRAYOID:
398 : 445 : argtypes[i] = INT4OID;
399 : 445 : 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;
157 tgl@sss.pgh.pa.us 408 :UBC 0 : case ANYMULTIRANGEOID:
409 : 0 : argtypes[i] = INT4MULTIRANGEOID;
410 : 0 : break;
157 tgl@sss.pgh.pa.us 411 :CBC 2175 : default:
412 : 2175 : break;
413 : : }
414 : : }
415 : : }
416 : 22263 : }
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 : 384 : delete_function(CachedFunction *func)
434 : : {
435 : : /* remove function from hash table (might be done already) */
436 : 384 : cfunc_hashtable_delete(func);
437 : :
438 : : /* release the function's storage if safe and not done already */
439 [ + - ]: 384 : if (func->use_count == 0 &&
440 [ + - ]: 384 : func->dcallback != NULL)
441 : : {
442 : 384 : func->dcallback(func);
443 : 384 : func->dcallback = NULL;
444 : : }
445 : 384 : }
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 : 89379 : 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 : 89379 : Oid funcOid = fcinfo->flinfo->fn_oid;
489 : : HeapTuple procTup;
490 : : Form_pg_proc procStruct;
491 : : CachedFunctionHashKey hashkey;
492 : 89379 : bool function_valid = false;
493 : 89379 : bool hashkey_valid = false;
101 494 : 89379 : bool new_function = false;
495 : :
496 : : /*
497 : : * Lookup the pg_proc tuple by Oid; we'll need it in any case
498 : : */
157 499 : 89379 : procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
500 [ - + ]: 89379 : if (!HeapTupleIsValid(procTup))
157 tgl@sss.pgh.pa.us 501 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcOid);
157 tgl@sss.pgh.pa.us 502 :CBC 89379 : 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 : 89379 : recheck:
509 [ + + ]: 89379 : if (!function)
510 : : {
511 : : /* Compute hashkey using function signature and actual arg types */
512 : 34260 : compute_function_hashkey(fcinfo, procStruct, &hashkey,
513 : : cacheEntrySize, includeResultType,
514 : : forValidator);
515 : 34260 : hashkey_valid = true;
516 : :
517 : : /* And do the lookup */
518 : 34260 : function = cfunc_hashtable_lookup(&hashkey);
519 : : }
520 : :
521 [ + + ]: 89379 : if (function)
522 : : {
523 : : /* We have a compiled function, but is it still valid? */
524 [ + + ]: 84663 : if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
525 [ + + ]: 84282 : ItemPointerEquals(&function->fn_tid, &procTup->t_self))
526 : 84279 : 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 : 384 : 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 [ - + ]: 384 : if (function->use_count != 0)
550 : : {
157 tgl@sss.pgh.pa.us 551 :UBC 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 : : */
157 tgl@sss.pgh.pa.us 561 [ + + ]:CBC 89379 : 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 [ - + ]: 5100 : if (!hashkey_valid)
157 tgl@sss.pgh.pa.us 568 :UBC 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 : : */
157 tgl@sss.pgh.pa.us 577 [ - + ]:CBC 5100 : Assert(cacheEntrySize >= sizeof(CachedFunction));
578 [ + + ]: 5100 : if (function == NULL)
579 : : {
580 : : function = (CachedFunction *)
581 : 4716 : MemoryContextAllocZero(TopMemoryContext, cacheEntrySize);
101 582 : 4716 : new_function = true;
583 : : }
584 : : else
585 : : {
586 : : /* re-using a previously existing struct, so clear it out */
157 587 : 384 : 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 : : */
101 598 [ + + ]: 5100 : PG_TRY();
599 : : {
600 : : /*
601 : : * Do the hard, language-specific part.
602 : : */
603 : 5100 : 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 [ - + ]: 5036 : 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 : 5036 : function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
619 : 5036 : function->fn_tid = procTup->t_self;
620 : 5036 : function->dcallback = dcallback;
621 : :
622 : : /*
623 : : * Add the completed struct to the hash table.
624 : : */
157 625 : 5036 : cfunc_hashtable_insert(function, &hashkey);
626 : : }
627 : :
628 : 89315 : ReleaseSysCache(procTup);
629 : :
630 : : /*
631 : : * Finally return the compiled function
632 : : */
633 : 89315 : return function;
634 : : }
|