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-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
582 tgl@sss.pgh.pa.us 59 :CBC 1036 : cfunc_hashtable_init(void)
60 : : {
61 : : HASHCTL ctl;
62 : :
63 : : /* don't allow double-initialization */
64 [ - + ]: 1036 : Assert(cfunc_hashtable == NULL);
65 : :
66 : 1036 : ctl.keysize = sizeof(CachedFunctionHashKey);
67 : 1036 : ctl.entrysize = sizeof(CachedFunctionHashEntry);
68 : 1036 : ctl.hash = cfunc_hash;
69 : 1036 : ctl.match = cfunc_match;
70 : 1036 : cfunc_hashtable = hash_create("Cached function hash",
71 : : FUNCS_PER_USER,
72 : : &ctl,
73 : : HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
74 : 1036 : }
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 : 49195 : cfunc_hash(const void *key, Size keysize)
86 : : {
87 : 49195 : const CachedFunctionHashKey *k = (const CachedFunctionHashKey *) key;
88 : : uint32 h;
89 : :
90 [ - + ]: 49195 : Assert(keysize == sizeof(CachedFunctionHashKey));
91 : : /* Hash all the fixed fields except callResultType */
43 john.naylor@postgres 92 :GNC 49195 : h = hash_bytes((const unsigned char *) k,
93 : : offsetof(CachedFunctionHashKey, callResultType));
94 : : /* Incorporate input argument types */
582 tgl@sss.pgh.pa.us 95 [ + + ]:CBC 49195 : if (k->nargs > 0)
96 : 26547 : h = hash_combine(h,
43 john.naylor@postgres 97 :GNC 26547 : hash_bytes((const unsigned char *) k->argtypes,
98 : 26547 : k->nargs * sizeof(Oid)));
99 : : /* Incorporate callResultType if present */
582 tgl@sss.pgh.pa.us 100 [ + + ]:CBC 49195 : if (k->callResultType)
101 : 1622 : h = hash_combine(h, hashRowType(k->callResultType));
102 : 49195 : return h;
103 : : }
104 : :
105 : : /*
106 : : * cfunc_match: match function to use with cfunc_hash
107 : : */
108 : : static int
109 : 37080 : cfunc_match(const void *key1, const void *key2, Size keysize)
110 : : {
111 : 37080 : const CachedFunctionHashKey *k1 = (const CachedFunctionHashKey *) key1;
112 : 37080 : const CachedFunctionHashKey *k2 = (const CachedFunctionHashKey *) key2;
113 : :
114 [ - + ]: 37080 : Assert(keysize == sizeof(CachedFunctionHashKey));
115 : : /* Compare all the fixed fields except callResultType */
116 [ - + ]: 37080 : if (memcmp(k1, k2, offsetof(CachedFunctionHashKey, callResultType)) != 0)
582 tgl@sss.pgh.pa.us 117 :UBC 0 : return 1; /* not equal */
118 : : /* Compare input argument types (we just verified that nargs matches) */
582 tgl@sss.pgh.pa.us 119 [ + + ]:CBC 37080 : if (k1->nargs > 0 &&
120 [ - + ]: 21510 : memcmp(k1->argtypes, k2->argtypes, k1->nargs * sizeof(Oid)) != 0)
582 tgl@sss.pgh.pa.us 121 :UBC 0 : return 1; /* not equal */
122 : : /* Compare callResultType */
582 tgl@sss.pgh.pa.us 123 [ + + ]:CBC 37080 : if (k1->callResultType)
124 : : {
125 [ + - ]: 862 : if (k2->callResultType)
126 : : {
127 [ + + ]: 862 : if (!equalRowTypes(k1->callResultType, k2->callResultType))
128 : 29 : return 1; /* not equal */
129 : : }
130 : : else
582 tgl@sss.pgh.pa.us 131 :UBC 0 : return 1; /* not equal */
132 : : }
133 : : else
134 : : {
582 tgl@sss.pgh.pa.us 135 [ - + ]:CBC 36218 : if (k2->callResultType)
582 tgl@sss.pgh.pa.us 136 :UBC 0 : return 1; /* not equal */
137 : : }
582 tgl@sss.pgh.pa.us 138 :CBC 37051 : 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 : 42910 : cfunc_hashtable_lookup(CachedFunctionHashKey *func_key)
147 : : {
148 : : CachedFunctionHashEntry *hentry;
149 : :
150 [ + + ]: 42910 : if (cfunc_hashtable == NULL)
151 : 1039 : return NULL;
152 : :
153 : 41871 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
154 : : func_key,
155 : : HASH_FIND,
156 : : NULL);
157 [ + + ]: 41871 : if (hentry)
158 : 36534 : return hentry->function;
159 : : else
160 : 5337 : return NULL;
161 : : }
162 : :
163 : : /*
164 : : * Insert a hash table entry.
165 : : */
166 : : static void
167 : 6807 : cfunc_hashtable_insert(CachedFunction *function,
168 : : CachedFunctionHashKey *func_key)
169 : : {
170 : : CachedFunctionHashEntry *hentry;
171 : : bool found;
172 : :
173 [ + + ]: 6807 : if (cfunc_hashtable == NULL)
174 : 1036 : cfunc_hashtable_init();
175 : :
176 : 6807 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
177 : : func_key,
178 : : HASH_ENTER,
179 : : &found);
180 [ - + ]: 6807 : if (found)
582 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 : : */
582 tgl@sss.pgh.pa.us 188 [ + + ]:CBC 6807 : if (func_key->callResultType)
189 : : {
190 : 427 : MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
191 : :
192 : 427 : hentry->key.callResultType = NULL;
193 : 427 : hentry->key.callResultType = CreateTupleDescCopy(func_key->callResultType);
194 : 427 : MemoryContextSwitchTo(oldcontext);
195 : : }
196 : :
197 : 6807 : hentry->function = function;
198 : :
199 : : /* Set back-link from function to hashtable key */
200 : 6807 : function->fn_hashkey = &hentry->key;
201 : 6807 : }
202 : :
203 : : /*
204 : : * Delete a hash table entry.
205 : : */
206 : : static void
207 : 517 : cfunc_hashtable_delete(CachedFunction *function)
208 : : {
209 : : CachedFunctionHashEntry *hentry;
210 : : TupleDesc tupdesc;
211 : :
212 : : /* do nothing if not in table */
213 [ - + ]: 517 : if (function->fn_hashkey == NULL)
582 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 : : */
582 tgl@sss.pgh.pa.us 222 :CBC 517 : tupdesc = function->fn_hashkey->callResultType;
223 : :
224 : 517 : hentry = (CachedFunctionHashEntry *) hash_search(cfunc_hashtable,
225 : 517 : function->fn_hashkey,
226 : : HASH_REMOVE,
227 : : NULL);
228 [ - + ]: 517 : if (hentry == NULL)
582 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 */
582 tgl@sss.pgh.pa.us 232 :CBC 517 : function->fn_hashkey = NULL;
233 : :
234 : : /* Release the callResultType if present */
235 [ + + ]: 517 : if (tupdesc)
236 : 20 : 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 : : * See comments for cached_function_compile() about how much of *fcinfo
247 : : * we're allowed to consider when forValidator is true.
248 : : */
249 : : static void
250 : 42910 : compute_function_hashkey(FunctionCallInfo fcinfo,
251 : : Form_pg_proc procStruct,
252 : : CachedFunctionHashKey *hashkey,
253 : : Size cacheEntrySize,
254 : : bool includeResultType,
255 : : bool forValidator)
256 : : {
257 : : /* Make sure pad bytes within fixed part of the struct are zero */
258 : 42910 : memset(hashkey, 0, offsetof(CachedFunctionHashKey, argtypes));
259 : :
260 : : /* get function OID */
261 : 42910 : hashkey->funcOid = fcinfo->flinfo->fn_oid;
262 : :
263 : : /* get call context */
264 [ + + + + ]: 42910 : hashkey->isTrigger = CALLED_AS_TRIGGER(fcinfo);
265 [ + + + + ]: 42910 : hashkey->isEventTrigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
266 : :
267 : : /* record cacheEntrySize so multiple languages can share hash table */
268 : 42910 : hashkey->cacheEntrySize = cacheEntrySize;
269 : :
270 : : /*
271 : : * If DML trigger, include trigger's OID in the hash, so that each trigger
272 : : * usage gets a different hash entry, allowing for e.g. different relation
273 : : * rowtypes or transition table names. In validation mode we do not know
274 : : * what relation or transition table names are intended to be used, so we
275 : : * leave trigOid zero; the hash entry built in this case will never be
276 : : * used for any actual calls.
277 : : *
278 : : * We don't currently need to distinguish different event trigger usages
279 : : * in the same way, since the special parameter variables don't vary in
280 : : * type in that case.
281 : : */
282 [ + + + + ]: 42910 : if (hashkey->isTrigger && !forValidator)
283 : : {
284 : 8841 : TriggerData *trigdata = (TriggerData *) fcinfo->context;
285 : :
286 : 8841 : hashkey->trigOid = trigdata->tg_trigger->tgoid;
287 : : }
288 : :
289 : : /* get input collation, if known */
290 : 42910 : hashkey->inputCollation = fcinfo->fncollation;
291 : :
292 : : /*
293 : : * We include only input arguments in the hash key, since output argument
294 : : * types can be deduced from those, and it would require extra cycles to
295 : : * include the output arguments. But we have to resolve any polymorphic
296 : : * argument types to the real types for the call.
297 : : */
298 [ + + ]: 42910 : if (procStruct->pronargs > 0)
299 : : {
300 : : /*
301 : : * Protect against overrun of fixed-size hashkey->argtypes array. This
302 : : * also protects later code in places such as PL/pgSQL. Ordinarily the
303 : : * parser would have checked this long since, but it's possible that
304 : : * we are looking at a pg_proc entry that was made by a server
305 : : * executable with a different value of FUNC_MAX_ARGS.
306 : : */
87 307 [ - + ]: 24101 : if (procStruct->pronargs > FUNC_MAX_ARGS)
87 tgl@sss.pgh.pa.us 308 [ # # ]:UBC 0 : ereport(ERROR,
309 : : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
310 : : errmsg_plural("cannot pass more than %d argument to a function",
311 : : "cannot pass more than %d arguments to a function",
312 : : FUNC_MAX_ARGS,
313 : : FUNC_MAX_ARGS)));
582 tgl@sss.pgh.pa.us 314 :CBC 24101 : hashkey->nargs = procStruct->pronargs;
315 : 24101 : memcpy(hashkey->argtypes, procStruct->proargtypes.values,
316 : 24101 : procStruct->pronargs * sizeof(Oid));
317 : 24101 : cfunc_resolve_polymorphic_argtypes(procStruct->pronargs,
318 : 24101 : hashkey->argtypes,
319 : : NULL, /* all args are inputs */
320 : 24101 : fcinfo->flinfo->fn_expr,
321 : : forValidator,
322 : 24101 : NameStr(procStruct->proname));
323 : : }
324 : :
325 : : /*
326 : : * While regular OUT arguments are sufficiently represented by the
327 : : * resolved input arguments, a function returning composite has additional
328 : : * variability: ALTER TABLE/ALTER TYPE could affect what it returns. Also,
329 : : * a function returning RECORD may depend on a column definition list to
330 : : * determine its output rowtype. If the caller needs the exact result
331 : : * type to be part of the hash lookup key, we must run
332 : : * get_call_result_type() to find that out. However, in validation mode
333 : : * the supplied fcinfo will likely not be complete enough for that to
334 : : * work, so leave callResultType unfilled (implying that the resulting
335 : : * hash entry will never match any actual calls for such functions).
336 : : */
4 337 [ + + + + ]: 42910 : if (includeResultType && !forValidator)
338 : : {
339 : : Oid resultTypeId;
340 : : TupleDesc tupdesc;
341 : :
582 342 [ + + ]: 20662 : switch (get_call_result_type(fcinfo, &resultTypeId, &tupdesc))
343 : : {
344 : 1220 : case TYPEFUNC_COMPOSITE:
345 : : case TYPEFUNC_COMPOSITE_DOMAIN:
346 : 1220 : hashkey->callResultType = tupdesc;
347 : 1220 : break;
348 : 19442 : default:
349 : : /* scalar result, or indeterminate rowtype */
350 : 19442 : break;
351 : : }
352 : : }
353 : 42910 : }
354 : :
355 : : /*
356 : : * This is the same as the standard resolve_polymorphic_argtypes() function,
357 : : * except that:
358 : : * 1. We go ahead and report the error if we can't resolve the types.
359 : : * 2. We treat RECORD-type input arguments (not output arguments) as if
360 : : * they were polymorphic, replacing their types with the actual input
361 : : * types if we can determine those. This allows us to create a separate
362 : : * function cache entry for each named composite type passed to such an
363 : : * argument.
364 : : * 3. In validation mode, we have no inputs to look at, so assume that
365 : : * polymorphic arguments are integer, integer-array or integer-range.
366 : : */
367 : : void
368 : 26577 : cfunc_resolve_polymorphic_argtypes(int numargs,
369 : : Oid *argtypes, char *argmodes,
370 : : Node *call_expr, bool forValidator,
371 : : const char *proname)
372 : : {
373 : : int i;
374 : :
375 [ + + ]: 26577 : if (!forValidator)
376 : : {
377 : : int inargno;
378 : :
379 : : /* normal case, pass to standard routine */
380 [ - + ]: 23499 : if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
381 : : call_expr))
582 tgl@sss.pgh.pa.us 382 [ # # ]:UBC 0 : ereport(ERROR,
383 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
384 : : errmsg("could not determine actual argument "
385 : : "type for polymorphic function \"%s\"",
386 : : proname)));
387 : : /* also, treat RECORD inputs (but not outputs) as polymorphic */
582 tgl@sss.pgh.pa.us 388 :CBC 23499 : inargno = 0;
389 [ + + ]: 64820 : for (i = 0; i < numargs; i++)
390 : : {
391 [ + + ]: 41321 : char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
392 : :
393 [ + + + + ]: 41321 : if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
394 : 88 : continue;
395 [ + + - + ]: 41233 : if (argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID)
396 : : {
397 : 357 : Oid resolvedtype = get_call_expr_argtype(call_expr,
398 : : inargno);
399 : :
400 [ + - ]: 357 : if (OidIsValid(resolvedtype))
401 : 357 : argtypes[i] = resolvedtype;
402 : : }
403 : 41233 : inargno++;
404 : : }
405 : : }
406 : : else
407 : : {
408 : : /* special validation case (no need to do anything for RECORD) */
409 [ + + ]: 6905 : for (i = 0; i < numargs; i++)
410 : : {
411 [ + + + - : 3827 : switch (argtypes[i])
+ ]
412 : : {
413 : 503 : case ANYELEMENTOID:
414 : : case ANYNONARRAYOID:
415 : : case ANYENUMOID: /* XXX dubious */
416 : : case ANYCOMPATIBLEOID:
417 : : case ANYCOMPATIBLENONARRAYOID:
418 : 503 : argtypes[i] = INT4OID;
419 : 503 : break;
420 : 137 : case ANYARRAYOID:
421 : : case ANYCOMPATIBLEARRAYOID:
422 : 137 : argtypes[i] = INT4ARRAYOID;
423 : 137 : break;
424 : 40 : case ANYRANGEOID:
425 : : case ANYCOMPATIBLERANGEOID:
426 : 40 : argtypes[i] = INT4RANGEOID;
427 : 40 : break;
582 tgl@sss.pgh.pa.us 428 :UBC 0 : case ANYMULTIRANGEOID:
429 : : case ANYCOMPATIBLEMULTIRANGEOID:
430 : 0 : argtypes[i] = INT4MULTIRANGEOID;
431 : 0 : break;
582 tgl@sss.pgh.pa.us 432 :CBC 3147 : default:
433 : 3147 : break;
434 : : }
435 : : }
436 : : }
437 : 26577 : }
438 : :
439 : : /*
440 : : * delete_function - clean up as much as possible of a stale function cache
441 : : *
442 : : * We can't release the CachedFunction struct itself, because of the
443 : : * possibility that there are fn_extra pointers to it. We can release
444 : : * the subsidiary storage, but only if there are no active evaluations
445 : : * in progress. Otherwise we'll just leak that storage. Since the
446 : : * case would only occur if a pg_proc update is detected during a nested
447 : : * recursive call on the function, a leak seems acceptable.
448 : : *
449 : : * Note that this can be called more than once if there are multiple fn_extra
450 : : * pointers to the same function cache. Hence be careful not to do things
451 : : * twice.
452 : : */
453 : : static void
454 : 517 : delete_function(CachedFunction *func)
455 : : {
456 : : /* remove function from hash table (might be done already) */
457 : 517 : cfunc_hashtable_delete(func);
458 : :
459 : : /* release the function's storage if safe and not done already */
460 [ + + ]: 517 : if (func->use_count == 0 &&
461 [ + - ]: 516 : func->dcallback != NULL)
462 : : {
463 : 516 : func->dcallback(func);
464 : 516 : func->dcallback = NULL;
465 : : }
466 : 517 : }
467 : :
468 : : /*
469 : : * Compile a cached function, if no existing cache entry is suitable.
470 : : *
471 : : * fcinfo is the current call information.
472 : : *
473 : : * function should be NULL or the result of a previous call of
474 : : * cached_function_compile() for the same fcinfo. The caller will
475 : : * typically save the result in fcinfo->flinfo->fn_extra, or in a
476 : : * field of a struct pointed to by fn_extra, to re-use in later
477 : : * calls within the same query.
478 : : *
479 : : * ccallback and dcallback are function-language-specific callbacks to
480 : : * compile and delete a cached function entry. dcallback can be NULL
481 : : * if there's nothing for it to do.
482 : : *
483 : : * cacheEntrySize is the function-language-specific size of the cache entry
484 : : * (which embeds a CachedFunction struct and typically has many more fields
485 : : * after that).
486 : : *
487 : : * If includeResultType is true and the function returns composite,
488 : : * include the actual result descriptor in the cache lookup key.
489 : : *
490 : : * If forValidator is true, we're only compiling for validation purposes,
491 : : * and so some checks are skipped. In this case the passed fcinfo need
492 : : * only provide the function OID and indications of whether the function
493 : : * is a trigger or event trigger (as tested via CALLED_AS_TRIGGER or
494 : : * CALLED_AS_EVENT_TRIGGER).
495 : : *
496 : : * Note: it's important for this to fall through quickly if the function
497 : : * has already been compiled.
498 : : *
499 : : * Note: this function leaves the "use_count" field as zero. The caller
500 : : * is expected to increment the use_count and decrement it when done with
501 : : * the cache entry.
502 : : */
503 : : CachedFunction *
504 : 107866 : cached_function_compile(FunctionCallInfo fcinfo,
505 : : CachedFunction *function,
506 : : CachedFunctionCompileCallback ccallback,
507 : : CachedFunctionDeleteCallback dcallback,
508 : : Size cacheEntrySize,
509 : : bool includeResultType,
510 : : bool forValidator)
511 : : {
512 : 107866 : Oid funcOid = fcinfo->flinfo->fn_oid;
513 : : HeapTuple procTup;
514 : : Form_pg_proc procStruct;
515 : : CachedFunctionHashKey hashkey;
516 : 107866 : bool function_valid = false;
517 : 107866 : bool hashkey_valid = false;
526 518 : 107866 : bool new_function = false;
519 : :
520 : : /*
521 : : * Lookup the pg_proc tuple by Oid; we'll need it in any case
522 : : */
582 523 : 107866 : procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
524 [ - + ]: 107866 : if (!HeapTupleIsValid(procTup))
582 tgl@sss.pgh.pa.us 525 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcOid);
582 tgl@sss.pgh.pa.us 526 :CBC 107866 : procStruct = (Form_pg_proc) GETSTRUCT(procTup);
527 : :
528 : : /*
529 : : * Do we already have a cache entry for the current FmgrInfo? If not, try
530 : : * to find one in the hash table.
531 : : */
532 : 107866 : recheck:
533 [ + + ]: 107866 : if (!function)
534 : : {
535 : : /* Compute hashkey using function signature and actual arg types */
536 : 42910 : compute_function_hashkey(fcinfo, procStruct, &hashkey,
537 : : cacheEntrySize, includeResultType,
538 : : forValidator);
539 : 42910 : hashkey_valid = true;
540 : :
541 : : /* And do the lookup */
542 : 42910 : function = cfunc_hashtable_lookup(&hashkey);
543 : : }
544 : :
545 [ + + ]: 107866 : if (function)
546 : : {
547 : : /* We have a compiled function, but is it still valid? */
548 [ + + ]: 101490 : if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
549 [ + + ]: 100985 : ItemPointerEquals(&function->fn_tid, &procTup->t_self))
550 : 100973 : function_valid = true;
551 : : else
552 : : {
553 : : /*
554 : : * Nope, so remove it from hashtable and try to drop associated
555 : : * storage (if not done already).
556 : : */
557 : 517 : delete_function(function);
558 : :
559 : : /*
560 : : * If the function isn't in active use then we can overwrite the
561 : : * func struct with new data, allowing any other existing fn_extra
562 : : * pointers to make use of the new definition on their next use.
563 : : * If it is in use then just leave it alone and make a new one.
564 : : * (The active invocations will run to completion using the
565 : : * previous definition, and then the cache entry will just be
566 : : * leaked; doesn't seem worth adding code to clean it up, given
567 : : * what a corner case this is.)
568 : : *
569 : : * If we found the function struct via fn_extra then it's possible
570 : : * a replacement has already been made, so go back and recheck the
571 : : * hashtable.
572 : : */
573 [ + + ]: 517 : if (function->use_count != 0)
574 : : {
575 : 1 : function = NULL;
576 [ - + ]: 1 : if (!hashkey_valid)
582 tgl@sss.pgh.pa.us 577 :UBC 0 : goto recheck;
578 : : }
579 : : }
580 : : }
581 : :
582 : : /*
583 : : * If the function wasn't found or was out-of-date, we have to compile it.
584 : : */
582 tgl@sss.pgh.pa.us 585 [ + + ]:CBC 107866 : if (!function_valid)
586 : : {
587 : : /*
588 : : * Calculate hashkey if we didn't already; we'll need it to store the
589 : : * completed function.
590 : : */
591 [ - + ]: 6893 : if (!hashkey_valid)
582 tgl@sss.pgh.pa.us 592 :UBC 0 : compute_function_hashkey(fcinfo, procStruct, &hashkey,
593 : : cacheEntrySize, includeResultType,
594 : : forValidator);
595 : :
596 : : /*
597 : : * Create the new function struct, if not done already. The function
598 : : * cache entry will be kept for the life of the backend, so put it in
599 : : * TopMemoryContext.
600 : : */
582 tgl@sss.pgh.pa.us 601 [ - + ]:CBC 6893 : Assert(cacheEntrySize >= sizeof(CachedFunction));
602 [ + + ]: 6893 : if (function == NULL)
603 : : {
604 : : function = (CachedFunction *)
605 : 6377 : MemoryContextAllocZero(TopMemoryContext, cacheEntrySize);
526 606 : 6377 : new_function = true;
607 : : }
608 : : else
609 : : {
610 : : /* re-using a previously existing struct, so clear it out */
582 611 : 516 : memset(function, 0, cacheEntrySize);
612 : : }
613 : :
614 : : /*
615 : : * However, if function compilation fails, we'd like not to leak the
616 : : * function struct, so use a PG_TRY block to prevent that. (It's up
617 : : * to the compile callback function to avoid its own internal leakage
618 : : * in such cases.) Unfortunately, freeing the struct is only safe if
619 : : * we just allocated it: otherwise there are probably fn_extra
620 : : * pointers to it.
621 : : */
526 622 [ + + ]: 6893 : PG_TRY();
623 : : {
624 : : /*
625 : : * Do the hard, language-specific part.
626 : : */
627 : 6893 : ccallback(fcinfo, procTup, &hashkey, function, forValidator);
628 : : }
629 : 86 : PG_CATCH();
630 : : {
631 [ + - ]: 86 : if (new_function)
632 : 86 : pfree(function);
633 : 86 : PG_RE_THROW();
634 : : }
635 [ - + ]: 6807 : PG_END_TRY();
636 : :
637 : : /*
638 : : * Fill in the CachedFunction part. (We do this last to prevent the
639 : : * function from looking valid before it's fully built.) fn_hashkey
640 : : * will be set by cfunc_hashtable_insert; use_count remains zero.
641 : : */
642 : 6807 : function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
643 : 6807 : function->fn_tid = procTup->t_self;
644 : 6807 : function->dcallback = dcallback;
645 : :
646 : : /*
647 : : * Add the completed struct to the hash table.
648 : : */
582 649 : 6807 : cfunc_hashtable_insert(function, &hashkey);
650 : : }
651 : :
652 : 107780 : ReleaseSysCache(procTup);
653 : :
654 : : /*
655 : : * Finally return the compiled function
656 : : */
657 : 107780 : return function;
658 : : }
|