Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * Python procedure manipulation for plpython
3 : : *
4 : : * src/pl/plpython/plpy_procedure.c
5 : : */
6 : :
7 : : #include "postgres.h"
8 : :
9 : : #include "catalog/pg_proc.h"
10 : : #include "catalog/pg_type.h"
11 : : #include "commands/event_trigger.h"
12 : : #include "commands/trigger.h"
13 : : #include "funcapi.h"
14 : : #include "plpy_elog.h"
15 : : #include "plpy_exec.h"
16 : : #include "plpy_main.h"
17 : : #include "plpy_procedure.h"
18 : : #include "plpy_util.h"
19 : : #include "utils/builtins.h"
20 : : #include "utils/funccache.h"
21 : : #include "utils/syscache.h"
22 : :
23 : : static void PLy_procedure_create(PLyProcedure *proc,
24 : : HeapTuple procTup,
25 : : Oid fn_oid,
26 : : PLyTrigType is_trigger);
27 : : static char *PLy_procedure_munge_source(const char *name, const char *src);
28 : : static void PLy_compile_callback(FunctionCallInfo fcinfo,
29 : : HeapTuple procTup,
30 : : const CachedFunctionHashKey *hashkey,
31 : : CachedFunction *cfunc,
32 : : bool forValidator);
33 : : static void PLy_delete_callback(CachedFunction *cfunc);
34 : : static void RemovePLyProcedureCache(void *arg);
35 : :
36 : :
37 : : /*
38 : : * PLy_procedure_name: get the name of the specified procedure.
39 : : *
40 : : * NB: this returns the SQL name, not the internal Python procedure name
41 : : */
42 : : char *
5308 peter_e@gmx.net 43 :CBC 551 : PLy_procedure_name(PLyProcedure *proc)
44 : : {
45 [ - + ]: 551 : if (proc == NULL)
5308 peter_e@gmx.net 46 :UBC 0 : return "<unknown procedure>";
5308 peter_e@gmx.net 47 :CBC 551 : return proc->proname;
48 : : }
49 : :
50 : : /*
51 : : * PLy_procedure_get: returns a PLyProcedureCache struct for the function,
52 : : * making it valid if necessary.
53 : : *
54 : : * The PLyProcedureCache contains a pointer to the long-lived PLyProcedure
55 : : * (managed by funccache.c) and execution-specific state like SRF state.
56 : : *
57 : : * For SRFs, if we are resuming execution (srfstate->iter != NULL), we skip
58 : : * revalidation and continue using the same PLyProcedure to ensure consistent
59 : : * behavior throughout the SRF execution.
60 : : */
61 : : PLyProcedureCache *
9 tgl@sss.pgh.pa.us 62 :GNC 994 : PLy_procedure_get(FunctionCallInfo fcinfo, bool forValidator)
63 : : {
64 : 994 : FmgrInfo *finfo = fcinfo->flinfo;
65 : : PLyProcedureCache *pcache;
66 : : PLyProcedure *proc;
67 : :
68 : : /*
69 : : * If this is the first execution for this FmgrInfo, set up a cache struct
70 : : * (initially containing null pointers). The cache must live as long as
71 : : * the FmgrInfo, so it goes in fn_mcxt. Also set up a memory context
72 : : * callback that will be invoked when fn_mcxt is reset/deleted.
73 : : */
74 : 994 : pcache = finfo->fn_extra;
75 [ + + ]: 994 : if (pcache == NULL)
76 : : {
77 : : pcache = (PLyProcedureCache *)
78 : 799 : MemoryContextAllocZero(finfo->fn_mcxt, sizeof(PLyProcedureCache));
79 : :
80 : 799 : pcache->fcontext = finfo->fn_mcxt;
81 : 799 : pcache->mcb.func = RemovePLyProcedureCache;
82 : 799 : pcache->mcb.arg = pcache;
83 : :
84 : 799 : MemoryContextRegisterResetCallback(finfo->fn_mcxt, &pcache->mcb);
85 : :
86 : 799 : finfo->fn_extra = pcache;
87 : : }
88 : :
89 : : /*
90 : : * If we are resuming execution of a set-returning function, just keep
91 : : * using the same cache. We do not ask funccache.c to re-validate the
92 : : * PLyProcedure: we want to run to completion using the function's initial
93 : : * definition.
94 : : *
95 : : * A live iterator (srfstate->iter != NULL) reliably means a genuine
96 : : * resume: when an iteration ends for any reason, srfstate->iter is reset
97 : : * to NULL (see comments for PLy_function_cleanup_srfstate).
98 : : */
99 [ + + + - ]: 994 : if (pcache->srfstate != NULL && pcache->srfstate->iter != NULL)
100 : : {
101 [ - + ]: 156 : Assert(pcache->proc != NULL);
102 : 156 : return pcache;
103 : : }
104 : :
105 : : /*
106 : : * Look up, or re-validate, the long-lived hash entry. Like SQL-language
107 : : * functions, make the hash key depend on the result of
108 : : * get_call_result_type() when that's composite, so that we can safely
109 : : * assume that we'll build a new hash entry if the composite rowtype
110 : : * changes.
111 : : */
112 : : proc = (PLyProcedure *)
113 : 838 : cached_function_compile(fcinfo,
114 : 838 : (CachedFunction *) pcache->proc,
115 : : PLy_compile_callback,
116 : : PLy_delete_callback,
117 : : sizeof(PLyProcedure),
118 : : true,
119 : : forValidator);
120 : :
121 : : /*
122 : : * Install the hash pointer in the PLyProcedureCache, and increment its
123 : : * use count to reflect that. If cached_function_compile gave us back a
124 : : * different hash entry than we were using before, we must decrement that
125 : : * one's use count.
126 : : */
127 [ + + ]: 834 : if (proc != pcache->proc)
128 : : {
129 [ - + ]: 795 : if (pcache->proc != NULL)
130 : : {
9 tgl@sss.pgh.pa.us 131 [ # # ]:UNC 0 : Assert(pcache->proc->cfunc.use_count > 0);
132 : 0 : pcache->proc->cfunc.use_count--;
133 : : }
9 tgl@sss.pgh.pa.us 134 :GNC 795 : pcache->proc = proc;
135 : 795 : proc->cfunc.use_count++;
136 : : }
137 : :
138 : 834 : return pcache;
139 : : }
140 : :
141 : : /*
142 : : * Create (well, fill in) a new PLyProcedure structure
143 : : */
144 : : static void
145 : 350 : PLy_procedure_create(PLyProcedure *proc,
146 : : HeapTuple procTup,
147 : : Oid fn_oid,
148 : : PLyTrigType is_trigger)
149 : : {
150 : : char procName[NAMEDATALEN + 256];
151 : : Form_pg_proc procStruct;
152 : : MemoryContext cxt;
153 : : MemoryContext oldcxt;
154 : : int rv;
155 : : char *ptr;
156 : :
5308 peter_e@gmx.net 157 :CBC 350 : procStruct = (Form_pg_proc) GETSTRUCT(procTup);
158 : 700 : rv = snprintf(procName, sizeof(procName),
159 : : "__plpython_procedure_%s_%u",
160 : 350 : NameStr(procStruct->proname),
161 : : fn_oid);
162 [ + - - + ]: 350 : if (rv >= sizeof(procName) || rv < 0)
5308 peter_e@gmx.net 163 [ # # ]:UBC 0 : elog(ERROR, "procedure name would overrun buffer");
164 : :
165 : : /* Replace any not-legal-in-Python-names characters with '_' */
3787 tgl@sss.pgh.pa.us 166 [ + + ]:CBC 16344 : for (ptr = procName; *ptr; ptr++)
167 : : {
168 [ + + + + ]: 15994 : if (!((*ptr >= 'A' && *ptr <= 'Z') ||
169 [ + + - + ]: 15992 : (*ptr >= 'a' && *ptr <= 'z') ||
170 [ + + + + ]: 4302 : (*ptr >= '0' && *ptr <= '9')))
171 : 2434 : *ptr = '_';
172 : : }
173 : :
174 : : /* Create long-lived context that all procedure info will live in */
3017 175 : 350 : cxt = AllocSetContextCreate(TopMemoryContext,
176 : : "PL/Python function",
177 : : ALLOCSET_DEFAULT_SIZES);
178 : :
3890 179 : 350 : oldcxt = MemoryContextSwitchTo(cxt);
180 : :
181 : 350 : proc->mcxt = cxt;
182 : :
5308 peter_e@gmx.net 183 [ + + ]: 350 : PG_TRY();
184 : : {
185 : : Datum protrftypes_datum;
186 : : Datum prosrcdatum;
187 : : bool isnull;
188 : : char *procSource;
189 : : int i;
190 : :
3890 tgl@sss.pgh.pa.us 191 : 350 : proc->proname = pstrdup(NameStr(procStruct->proname));
3017 192 : 350 : MemoryContextSetIdentifier(cxt, proc->proname);
3890 193 : 350 : proc->pyname = pstrdup(procName);
3738 194 : 350 : proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
195 : 350 : proc->is_setof = procStruct->proretset;
3042 peter_e@gmx.net 196 : 350 : proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
784 tgl@sss.pgh.pa.us 197 : 350 : proc->is_trigger = is_trigger;
3738 198 : 350 : proc->src = NULL;
199 : 350 : proc->argnames = NULL;
3148 200 : 350 : proc->args = NULL;
3890 201 : 350 : proc->nargs = 0;
202 : 350 : proc->langid = procStruct->prolang;
203 : 350 : protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
204 : : Anum_pg_proc_protrftypes,
205 : : &isnull);
206 [ + + ]: 350 : proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
3738 207 : 350 : proc->code = NULL;
208 : 350 : proc->statics = NULL;
3890 209 : 350 : proc->globals = NULL;
3738 210 : 350 : proc->calldepth = 0;
211 : 350 : proc->argstack = NULL;
212 : :
213 : : /*
214 : : * get information required for output conversion of the return value,
215 : : * but only if this isn't a trigger.
216 : : */
313 peter@eisentraut.org 217 [ + + ]:GNC 350 : if (is_trigger == PLPY_NOT_TRIGGER)
218 : : {
3148 tgl@sss.pgh.pa.us 219 :CBC 296 : Oid rettype = procStruct->prorettype;
220 : : HeapTuple rvTypeTup;
221 : : Form_pg_type rvTypeStruct;
222 : :
223 : 296 : rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
5308 peter_e@gmx.net 224 [ - + ]: 296 : if (!HeapTupleIsValid(rvTypeTup))
3148 tgl@sss.pgh.pa.us 225 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", rettype);
5308 peter_e@gmx.net 226 :CBC 296 : rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
227 : :
228 : : /* Disallow pseudotype result, except for void or record */
229 [ + + ]: 296 : if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
230 : : {
3148 tgl@sss.pgh.pa.us 231 [ + + + + ]: 72 : if (rettype == VOIDOID ||
232 : : rettype == RECORDOID)
233 : : /* okay */ ;
2070 234 [ - + - - ]: 1 : else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
5308 peter_e@gmx.net 235 [ + - ]: 1 : ereport(ERROR,
236 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
237 : : errmsg("trigger functions can only be called as triggers")));
238 : : else
5308 peter_e@gmx.net 239 [ # # ]:UBC 0 : ereport(ERROR,
240 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
241 : : errmsg("PL/Python functions cannot return type %s",
242 : : format_type_be(rettype))));
243 : : }
244 : :
245 : : /* set up output function for procedure result */
3148 tgl@sss.pgh.pa.us 246 :CBC 295 : PLy_output_setup_func(&proc->result, proc->mcxt,
247 : : rettype, -1, proc);
248 : :
5308 peter_e@gmx.net 249 : 295 : ReleaseSysCache(rvTypeTup);
250 : : }
251 : : else
252 : : {
253 : : /*
254 : : * In a trigger function, we use proc->result and proc->result_in
255 : : * for converting tuples, but we don't yet have enough info to set
256 : : * them up. PLy_exec_trigger will deal with it.
257 : : */
3148 tgl@sss.pgh.pa.us 258 : 54 : proc->result.typoid = InvalidOid;
259 : 54 : proc->result_in.typoid = InvalidOid;
260 : : }
261 : :
262 : : /*
263 : : * Now get information required for input conversion of the
264 : : * procedure's arguments. Note that we ignore output arguments here.
265 : : * If the function returns record, those I/O functions will be set up
266 : : * when the function is first called.
267 : : */
5308 peter_e@gmx.net 268 [ + + ]: 349 : if (procStruct->pronargs)
269 : : {
270 : : Oid *types;
271 : : char **names,
272 : : *modes;
273 : : int pos,
274 : : total;
275 : :
276 : : /* extract argument type info from the pg_proc tuple */
277 : 161 : total = get_func_arg_info(procTup, &types, &names, &modes);
278 : :
279 : : /* count number of in+inout args into proc->nargs */
280 [ + + ]: 161 : if (modes == NULL)
281 : 144 : proc->nargs = total;
282 : : else
283 : : {
284 : : /* proc->nargs was initialized to 0 above */
285 [ + + ]: 64 : for (i = 0; i < total; i++)
286 : : {
1846 tgl@sss.pgh.pa.us 287 [ + + ]: 47 : if (modes[i] != PROARGMODE_OUT &&
5308 peter_e@gmx.net 288 [ + - ]: 29 : modes[i] != PROARGMODE_TABLE)
289 : 29 : (proc->nargs)++;
290 : : }
291 : : }
292 : :
293 : : /* Allocate arrays for per-input-argument data */
203 michael@paquier.xyz 294 :GNC 161 : proc->argnames = (char **) palloc0_array(char *, proc->nargs);
295 : 161 : proc->args = (PLyDatumToOb *) palloc0_array(PLyDatumToOb, proc->nargs);
296 : :
5308 peter_e@gmx.net 297 [ + + ]:CBC 425 : for (i = pos = 0; i < total; i++)
298 : : {
299 : : HeapTuple argTypeTup;
300 : : Form_pg_type argTypeStruct;
301 : :
302 [ + + ]: 264 : if (modes &&
1846 tgl@sss.pgh.pa.us 303 [ + + ]: 47 : (modes[i] == PROARGMODE_OUT ||
5308 peter_e@gmx.net 304 [ - + ]: 29 : modes[i] == PROARGMODE_TABLE))
305 : 18 : continue; /* skip OUT arguments */
306 : :
307 [ - + ]: 246 : Assert(types[i] == procStruct->proargtypes.values[pos]);
308 : :
309 : 246 : argTypeTup = SearchSysCache1(TYPEOID,
310 : 246 : ObjectIdGetDatum(types[i]));
311 [ - + ]: 246 : if (!HeapTupleIsValid(argTypeTup))
5308 peter_e@gmx.net 312 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for type %u", types[i]);
5308 peter_e@gmx.net 313 :CBC 246 : argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
314 : :
315 : : /* disallow pseudotype arguments */
3148 tgl@sss.pgh.pa.us 316 [ - + ]: 246 : if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
3148 tgl@sss.pgh.pa.us 317 [ # # ]:UBC 0 : ereport(ERROR,
318 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
319 : : errmsg("PL/Python functions cannot accept type %s",
320 : : format_type_be(types[i]))));
321 : :
322 : : /* set up I/O function info */
3148 tgl@sss.pgh.pa.us 323 :CBC 246 : PLy_input_setup_func(&proc->args[pos], proc->mcxt,
324 : 246 : types[i], -1, /* typmod not known */
325 : : proc);
326 : :
327 : : /* get argument name */
3890 328 [ + + ]: 246 : proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
329 : :
5308 peter_e@gmx.net 330 : 246 : ReleaseSysCache(argTypeTup);
331 : :
332 : 246 : pos++;
333 : : }
334 : : }
335 : :
336 : : /*
337 : : * get the text of the function.
338 : : */
1193 dgustafsson@postgres 339 : 349 : prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
340 : : Anum_pg_proc_prosrc);
5308 peter_e@gmx.net 341 : 349 : procSource = TextDatumGetCString(prosrcdatum);
342 : :
343 : 349 : PLy_procedure_compile(proc, procSource);
344 : :
345 : 346 : pfree(procSource);
346 : : }
347 : 4 : PG_CATCH();
348 : : {
3890 tgl@sss.pgh.pa.us 349 : 4 : MemoryContextSwitchTo(oldcxt);
5308 peter_e@gmx.net 350 : 4 : PLy_procedure_delete(proc);
351 : 4 : PG_RE_THROW();
352 : : }
353 [ - + ]: 346 : PG_END_TRY();
354 : :
3890 tgl@sss.pgh.pa.us 355 : 346 : MemoryContextSwitchTo(oldcxt);
5308 peter_e@gmx.net 356 :GIC 346 : }
357 : :
358 : : /*
359 : : * Insert the procedure into the Python interpreter
360 : : */
361 : : void
5308 peter_e@gmx.net 362 :CBC 370 : PLy_procedure_compile(PLyProcedure *proc, const char *src)
363 : : {
364 : 370 : PyObject *crv = NULL;
365 : : char *msrc;
366 : : PyObject *code0;
367 : :
368 : 370 : proc->globals = PyDict_Copy(PLy_interp_globals);
369 : :
370 : : /*
371 : : * SD is private preserved data between calls. GD is global data shared by
372 : : * all functions
373 : : */
374 : 370 : proc->statics = PyDict_New();
3164 375 [ - + ]: 370 : if (!proc->statics)
3164 peter_e@gmx.net 376 :UBC 0 : PLy_elog(ERROR, NULL);
5308 peter_e@gmx.net 377 :CBC 370 : PyDict_SetItemString(proc->globals, "SD", proc->statics);
378 : :
379 : : /*
380 : : * insert the function code into the interpreter
381 : : */
382 : 370 : msrc = PLy_procedure_munge_source(proc->pyname, src);
383 : : /* Save the mangled source for later inclusion in tracebacks */
3890 tgl@sss.pgh.pa.us 384 : 370 : proc->src = MemoryContextStrdup(proc->mcxt, msrc);
475 peter@eisentraut.org 385 : 370 : code0 = Py_CompileString(msrc, "<string>", Py_file_input);
386 [ + + ]: 370 : if (code0)
387 : 367 : crv = PyEval_EvalCode(code0, proc->globals, NULL);
5308 peter_e@gmx.net 388 : 370 : pfree(msrc);
389 : :
390 [ + + ]: 370 : if (crv != NULL)
391 : : {
392 : : int clen;
393 : : char call[NAMEDATALEN + 256];
394 : :
395 : : Py_DECREF(crv);
396 : :
397 : : /*
398 : : * compile a call to the function
399 : : */
400 : 367 : clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
401 [ + - - + ]: 367 : if (clen < 0 || clen >= sizeof(call))
5308 peter_e@gmx.net 402 [ # # ]:UBC 0 : elog(ERROR, "string would overflow buffer");
5308 peter_e@gmx.net 403 :CBC 367 : proc->code = Py_CompileString(call, "<string>", Py_eval_input);
404 [ + - ]: 367 : if (proc->code != NULL)
405 : 367 : return;
406 : : }
407 : :
408 [ + - ]: 3 : if (proc->proname)
409 : 3 : PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
410 : : proc->proname);
411 : : else
5308 peter_e@gmx.net 412 :UBC 0 : PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
413 : : }
414 : :
415 : : void
5308 peter_e@gmx.net 416 :CBC 30 : PLy_procedure_delete(PLyProcedure *proc)
417 : : {
418 : 30 : Py_XDECREF(proc->code);
419 : 30 : Py_XDECREF(proc->statics);
420 : 30 : Py_XDECREF(proc->globals);
3890 tgl@sss.pgh.pa.us 421 : 30 : MemoryContextDelete(proc->mcxt);
5308 peter_e@gmx.net 422 : 30 : }
423 : :
424 : : static char *
425 : 370 : PLy_procedure_munge_source(const char *name, const char *src)
426 : : {
427 : : char *mrc,
428 : : *mp;
429 : : const char *sp;
430 : : size_t mlen;
431 : : int plen;
432 : :
433 : : /*
434 : : * room for function source and the def statement
435 : : */
436 : 370 : mlen = (strlen(src) * 2) + strlen(name) + 16;
437 : :
438 : 370 : mrc = palloc(mlen);
439 : 370 : plen = snprintf(mrc, mlen, "def %s():\n\t", name);
440 [ + - - + ]: 370 : Assert(plen >= 0 && plen < mlen);
441 : :
442 : 370 : sp = src;
443 : 370 : mp = mrc + plen;
444 : :
445 [ + + ]: 43002 : while (*sp != '\0')
446 : : {
447 [ + + + + ]: 42632 : if (*sp == '\r' && *(sp + 1) == '\n')
448 : 3 : sp++;
449 : :
450 [ + + + + ]: 42632 : if (*sp == '\n' || *sp == '\r')
451 : : {
452 : 1810 : *mp++ = '\n';
453 : 1810 : *mp++ = '\t';
454 : 1810 : sp++;
455 : : }
456 : : else
457 : 40822 : *mp++ = *sp++;
458 : : }
459 : 370 : *mp++ = '\n';
460 : 370 : *mp++ = '\n';
461 : 370 : *mp = '\0';
462 : :
463 [ - + ]: 370 : if (mp > (mrc + mlen))
2521 michael@paquier.xyz 464 [ # # ]:UBC 0 : elog(FATAL, "buffer overrun in PLy_procedure_munge_source");
465 : :
5308 peter_e@gmx.net 466 :CBC 370 : return mrc;
467 : : }
468 : :
469 : : /*
470 : : * Compile callback for funccache.c.
471 : : *
472 : : * cached_function_compile() calls this when it needs to (re)compile the
473 : : * long-lived PLyProcedure for a function. The CachedFunction handed to us is
474 : : * pre-zeroed workspace of size sizeof(PLyProcedure); we just have to fill in
475 : : * the PL/Python-specific fields.
476 : : */
477 : : static void
9 tgl@sss.pgh.pa.us 478 :GNC 350 : PLy_compile_callback(FunctionCallInfo fcinfo,
479 : : HeapTuple procTup,
480 : : const CachedFunctionHashKey *hashkey,
481 : : CachedFunction *cfunc,
482 : : bool forValidator)
483 : : {
484 : 350 : PLyProcedure *proc = (PLyProcedure *) cfunc;
485 : 350 : Oid fn_oid = fcinfo->flinfo->fn_oid;
486 : : PLyTrigType is_trigger;
487 : :
488 : : /*
489 : : * Derive the trigger type from the call context, matching what
490 : : * plpython3_call_handler dispatches on.
491 : : */
492 [ + + + + ]: 350 : if (CALLED_AS_TRIGGER(fcinfo))
493 : 53 : is_trigger = PLPY_TRIGGER;
494 [ + + + + ]: 297 : else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
495 : 1 : is_trigger = PLPY_EVENT_TRIGGER;
496 : : else
497 : 296 : is_trigger = PLPY_NOT_TRIGGER;
498 : :
499 : 350 : PLy_procedure_create(proc, procTup, fn_oid, is_trigger);
500 : 346 : }
501 : :
502 : : /*
503 : : * Deletion callback for funccache.c.
504 : : *
505 : : * cached_function_compile() calls this when it discards a cache entry, which
506 : : * only happens once the entry's use count has dropped to zero. We must free
507 : : * the subsidiary data but not the CachedFunction struct itself.
508 : : */
509 : : static void
510 : 5 : PLy_delete_callback(CachedFunction *cfunc)
511 : : {
512 : 5 : PLyProcedure *proc = (PLyProcedure *) cfunc;
513 : :
514 [ - + ]: 5 : Assert(proc->cfunc.use_count == 0);
515 [ - + ]: 5 : Assert(proc->calldepth == 0);
516 : :
517 : 5 : PLy_procedure_delete(proc);
518 : 5 : }
519 : :
520 : : /*
521 : : * MemoryContext callback function
522 : : *
523 : : * We register this in the memory context that contains a PLyProcedureCache
524 : : * struct (that is, the FmgrInfo's fn_mcxt). When the memory context is reset
525 : : * or deleted, we release the reference count (if any) that the cache holds on
526 : : * the long-lived hash entry. Note that this will happen even during error
527 : : * aborts.
528 : : *
529 : : * This is also our opportunity to release the Python references held by an
530 : : * interrupted set-returning function. ShutdownPLyFunction() handles that for
531 : : * routine in-query cancellation cases, but it does not run during an error
532 : : * abort; this callback does, so it is the backstop that prevents leaking the
533 : : * SRF's iterator and saved arguments when a query errors out mid-iteration.
534 : : */
535 : : static void
536 : 799 : RemovePLyProcedureCache(void *arg)
537 : : {
538 : 799 : PLyProcedureCache *pcache = (PLyProcedureCache *) arg;
539 : :
540 : : /* Release any Python state left behind by an interrupted SRF */
541 : 799 : PLy_function_cleanup_srfstate(pcache);
542 : :
543 : : /* Release reference count on PLyProcedure */
544 [ + + ]: 799 : if (pcache->proc != NULL)
545 : : {
546 [ - + ]: 530 : Assert(pcache->proc->cfunc.use_count > 0);
547 : 530 : pcache->proc->cfunc.use_count--;
548 : 530 : pcache->proc = NULL;
549 : : }
550 : :
551 : : /* We needn't free the pcache object itself, context cleanup does that */
552 : 799 : }
|