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 : }
|