LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_procedure.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 93.9 % 181 170
Test Date: 2026-07-18 10:15:36 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 71.4 % 126 90

             Branch data     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 *
      43                 :         551 : PLy_procedure_name(PLyProcedure *proc)
      44                 :             : {
      45         [ -  + ]:         551 :     if (proc == NULL)
      46                 :           0 :         return "<unknown procedure>";
      47                 :         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 *
      62                 :         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                 :             :         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                 :             :         {
     131                 :             :             Assert(pcache->proc->cfunc.use_count > 0);
     132                 :           0 :             pcache->proc->cfunc.use_count--;
     133                 :             :         }
     134                 :         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                 :             : 
     157                 :         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)
     163         [ #  # ]:           0 :         elog(ERROR, "procedure name would overrun buffer");
     164                 :             : 
     165                 :             :     /* Replace any not-legal-in-Python-names characters with '_' */
     166         [ +  + ]:       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 */
     175                 :         350 :     cxt = AllocSetContextCreate(TopMemoryContext,
     176                 :             :                                 "PL/Python function",
     177                 :             :                                 ALLOCSET_DEFAULT_SIZES);
     178                 :             : 
     179                 :         350 :     oldcxt = MemoryContextSwitchTo(cxt);
     180                 :             : 
     181                 :         350 :     proc->mcxt = cxt;
     182                 :             : 
     183         [ +  + ]:         350 :     PG_TRY();
     184                 :             :     {
     185                 :             :         Datum       protrftypes_datum;
     186                 :             :         Datum       prosrcdatum;
     187                 :             :         bool        isnull;
     188                 :             :         char       *procSource;
     189                 :             :         int         i;
     190                 :             : 
     191                 :         350 :         proc->proname = pstrdup(NameStr(procStruct->proname));
     192                 :         350 :         MemoryContextSetIdentifier(cxt, proc->proname);
     193                 :         350 :         proc->pyname = pstrdup(procName);
     194                 :         350 :         proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
     195                 :         350 :         proc->is_setof = procStruct->proretset;
     196                 :         350 :         proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
     197                 :         350 :         proc->is_trigger = is_trigger;
     198                 :         350 :         proc->src = NULL;
     199                 :         350 :         proc->argnames = NULL;
     200                 :         350 :         proc->args = NULL;
     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);
     207                 :         350 :         proc->code = NULL;
     208                 :         350 :         proc->statics = NULL;
     209                 :         350 :         proc->globals = NULL;
     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                 :             :          */
     217         [ +  + ]:         350 :         if (is_trigger == PLPY_NOT_TRIGGER)
     218                 :             :         {
     219                 :         296 :             Oid         rettype = procStruct->prorettype;
     220                 :             :             HeapTuple   rvTypeTup;
     221                 :             :             Form_pg_type rvTypeStruct;
     222                 :             : 
     223                 :         296 :             rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
     224         [ -  + ]:         296 :             if (!HeapTupleIsValid(rvTypeTup))
     225         [ #  # ]:           0 :                 elog(ERROR, "cache lookup failed for type %u", rettype);
     226                 :         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                 :             :             {
     231   [ +  +  +  + ]:          72 :                 if (rettype == VOIDOID ||
     232                 :             :                     rettype == RECORDOID)
     233                 :             :                      /* okay */ ;
     234   [ -  +  -  - ]:           1 :                 else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
     235         [ +  - ]:           1 :                     ereport(ERROR,
     236                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     237                 :             :                              errmsg("trigger functions can only be called as triggers")));
     238                 :             :                 else
     239         [ #  # ]:           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 */
     246                 :         295 :             PLy_output_setup_func(&proc->result, proc->mcxt,
     247                 :             :                                   rettype, -1, proc);
     248                 :             : 
     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                 :             :              */
     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                 :             :          */
     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                 :             :                 {
     287         [ +  + ]:          47 :                     if (modes[i] != PROARGMODE_OUT &&
     288         [ +  - ]:          29 :                         modes[i] != PROARGMODE_TABLE)
     289                 :          29 :                         (proc->nargs)++;
     290                 :             :                 }
     291                 :             :             }
     292                 :             : 
     293                 :             :             /* Allocate arrays for per-input-argument data */
     294                 :         161 :             proc->argnames = (char **) palloc0_array(char *, proc->nargs);
     295                 :         161 :             proc->args = (PLyDatumToOb *) palloc0_array(PLyDatumToOb, proc->nargs);
     296                 :             : 
     297         [ +  + ]:         425 :             for (i = pos = 0; i < total; i++)
     298                 :             :             {
     299                 :             :                 HeapTuple   argTypeTup;
     300                 :             :                 Form_pg_type argTypeStruct;
     301                 :             : 
     302         [ +  + ]:         264 :                 if (modes &&
     303         [ +  + ]:          47 :                     (modes[i] == PROARGMODE_OUT ||
     304         [ -  + ]:          29 :                      modes[i] == PROARGMODE_TABLE))
     305                 :          18 :                     continue;   /* skip OUT arguments */
     306                 :             : 
     307                 :             :                 Assert(types[i] == procStruct->proargtypes.values[pos]);
     308                 :             : 
     309                 :         246 :                 argTypeTup = SearchSysCache1(TYPEOID,
     310                 :         246 :                                              ObjectIdGetDatum(types[i]));
     311         [ -  + ]:         246 :                 if (!HeapTupleIsValid(argTypeTup))
     312         [ #  # ]:           0 :                     elog(ERROR, "cache lookup failed for type %u", types[i]);
     313                 :         246 :                 argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
     314                 :             : 
     315                 :             :                 /* disallow pseudotype arguments */
     316         [ -  + ]:         246 :                 if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
     317         [ #  # ]:           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 */
     323                 :         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 */
     328         [ +  + ]:         246 :                 proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
     329                 :             : 
     330                 :         246 :                 ReleaseSysCache(argTypeTup);
     331                 :             : 
     332                 :         246 :                 pos++;
     333                 :             :             }
     334                 :             :         }
     335                 :             : 
     336                 :             :         /*
     337                 :             :          * get the text of the function.
     338                 :             :          */
     339                 :         349 :         prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
     340                 :             :                                              Anum_pg_proc_prosrc);
     341                 :         349 :         procSource = TextDatumGetCString(prosrcdatum);
     342                 :             : 
     343                 :         349 :         PLy_procedure_compile(proc, procSource);
     344                 :             : 
     345                 :         346 :         pfree(procSource);
     346                 :             :     }
     347                 :           4 :     PG_CATCH();
     348                 :             :     {
     349                 :           4 :         MemoryContextSwitchTo(oldcxt);
     350                 :           4 :         PLy_procedure_delete(proc);
     351                 :           4 :         PG_RE_THROW();
     352                 :             :     }
     353         [ -  + ]:         346 :     PG_END_TRY();
     354                 :             : 
     355                 :         346 :     MemoryContextSwitchTo(oldcxt);
     356                 :         346 : }
     357                 :             : 
     358                 :             : /*
     359                 :             :  * Insert the procedure into the Python interpreter
     360                 :             :  */
     361                 :             : void
     362                 :         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();
     375         [ -  + ]:         370 :     if (!proc->statics)
     376                 :           0 :         PLy_elog(ERROR, NULL);
     377                 :         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 */
     384                 :         370 :     proc->src = MemoryContextStrdup(proc->mcxt, msrc);
     385                 :         370 :     code0 = Py_CompileString(msrc, "<string>", Py_file_input);
     386         [ +  + ]:         370 :     if (code0)
     387                 :         367 :         crv = PyEval_EvalCode(code0, proc->globals, NULL);
     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))
     402         [ #  # ]:           0 :             elog(ERROR, "string would overflow buffer");
     403                 :         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
     412                 :           0 :         PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
     413                 :             : }
     414                 :             : 
     415                 :             : void
     416                 :          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);
     421                 :          30 :     MemoryContextDelete(proc->mcxt);
     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                 :             :     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))
     464         [ #  # ]:           0 :         elog(FATAL, "buffer overrun in PLy_procedure_munge_source");
     465                 :             : 
     466                 :         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
     478                 :         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                 :             :     Assert(proc->cfunc.use_count == 0);
     515                 :             :     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                 :             :         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 : }
        

Generated by: LCOV version 2.0-1