LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_procedure.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.7 % 190 178
Test Date: 2026-03-07 03:14:56 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            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 "access/htup_details.h"
      10              : #include "catalog/pg_proc.h"
      11              : #include "catalog/pg_type.h"
      12              : #include "funcapi.h"
      13              : #include "plpy_elog.h"
      14              : #include "plpy_main.h"
      15              : #include "plpy_procedure.h"
      16              : #include "plpy_util.h"
      17              : #include "utils/builtins.h"
      18              : #include "utils/hsearch.h"
      19              : #include "utils/memutils.h"
      20              : #include "utils/syscache.h"
      21              : 
      22              : static HTAB *PLy_procedure_cache = NULL;
      23              : 
      24              : static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, PLyTrigType is_trigger);
      25              : static bool PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup);
      26              : static char *PLy_procedure_munge_source(const char *name, const char *src);
      27              : 
      28              : 
      29              : void
      30           23 : init_procedure_caches(void)
      31              : {
      32              :     HASHCTL     hash_ctl;
      33              : 
      34           23 :     hash_ctl.keysize = sizeof(PLyProcedureKey);
      35           23 :     hash_ctl.entrysize = sizeof(PLyProcedureEntry);
      36           23 :     PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
      37              :                                       HASH_ELEM | HASH_BLOBS);
      38           23 : }
      39              : 
      40              : /*
      41              :  * PLy_procedure_name: get the name of the specified procedure.
      42              :  *
      43              :  * NB: this returns the SQL name, not the internal Python procedure name
      44              :  */
      45              : char *
      46          531 : PLy_procedure_name(PLyProcedure *proc)
      47              : {
      48          531 :     if (proc == NULL)
      49            0 :         return "<unknown procedure>";
      50          531 :     return proc->proname;
      51              : }
      52              : 
      53              : /*
      54              :  * PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
      55              :  * returns a new PLyProcedure.
      56              :  *
      57              :  * fn_oid is the OID of the function requested
      58              :  * fn_rel is InvalidOid or the relation this function triggers on
      59              :  * is_trigger denotes whether the function is a trigger function
      60              :  *
      61              :  * The reason that both fn_rel and is_trigger need to be passed is that when
      62              :  * trigger functions get validated we don't know which relation(s) they'll
      63              :  * be used with, so no sensible fn_rel can be passed.  Also, in that case
      64              :  * we can't make a cache entry because we can't construct the right cache key.
      65              :  * To forestall leakage of the PLyProcedure in such cases, delete it after
      66              :  * construction and return NULL.  That's okay because the only caller that
      67              :  * would pass that set of values is plpython3_validator, which ignores our
      68              :  * result anyway.
      69              :  */
      70              : PLyProcedure *
      71          951 : PLy_procedure_get(Oid fn_oid, Oid fn_rel, PLyTrigType is_trigger)
      72              : {
      73              :     bool        use_cache;
      74              :     HeapTuple   procTup;
      75              :     PLyProcedureKey key;
      76          951 :     PLyProcedureEntry *volatile entry = NULL;
      77          951 :     PLyProcedure *volatile proc = NULL;
      78          951 :     bool        found = false;
      79              : 
      80          951 :     if (is_trigger == PLPY_TRIGGER && fn_rel == InvalidOid)
      81           24 :         use_cache = false;
      82              :     else
      83          927 :         use_cache = true;
      84              : 
      85          951 :     procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
      86          951 :     if (!HeapTupleIsValid(procTup))
      87            0 :         elog(ERROR, "cache lookup failed for function %u", fn_oid);
      88              : 
      89              :     /*
      90              :      * Look for the function in the cache, unless we don't have the necessary
      91              :      * information (e.g. during validation). In that case we just don't cache
      92              :      * anything.
      93              :      */
      94          951 :     if (use_cache)
      95              :     {
      96          927 :         key.fn_oid = fn_oid;
      97          927 :         key.fn_rel = fn_rel;
      98          927 :         entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
      99          927 :         proc = entry->proc;
     100              :     }
     101              : 
     102          951 :     PG_TRY();
     103              :     {
     104          951 :         if (!found)
     105              :         {
     106              :             /* Haven't found it, create a new procedure */
     107          274 :             proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
     108          270 :             if (use_cache)
     109          246 :                 entry->proc = proc;
     110              :             else
     111              :             {
     112              :                 /* Delete the proc, otherwise it's a memory leak */
     113           24 :                 PLy_procedure_delete(proc);
     114           24 :                 proc = NULL;
     115              :             }
     116              :         }
     117          677 :         else if (!PLy_procedure_valid(proc, procTup))
     118              :         {
     119              :             /* Found it, but it's invalid, free and reuse the cache entry */
     120            5 :             entry->proc = NULL;
     121            5 :             if (proc)
     122            5 :                 PLy_procedure_delete(proc);
     123            5 :             proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
     124            5 :             entry->proc = proc;
     125              :         }
     126              :         /* Found it and it's valid, it's fine to use it */
     127              :     }
     128            4 :     PG_CATCH();
     129              :     {
     130              :         /* Do not leave an uninitialized entry in the cache */
     131            4 :         if (use_cache)
     132            4 :             hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
     133            4 :         PG_RE_THROW();
     134              :     }
     135          947 :     PG_END_TRY();
     136              : 
     137          947 :     ReleaseSysCache(procTup);
     138              : 
     139          947 :     return proc;
     140              : }
     141              : 
     142              : /*
     143              :  * Create a new PLyProcedure structure
     144              :  */
     145              : static PLyProcedure *
     146          279 : PLy_procedure_create(HeapTuple procTup, Oid fn_oid, PLyTrigType is_trigger)
     147              : {
     148              :     char        procName[NAMEDATALEN + 256];
     149              :     Form_pg_proc procStruct;
     150              :     PLyProcedure *volatile proc;
     151              :     MemoryContext cxt;
     152              :     MemoryContext oldcxt;
     153              :     int         rv;
     154              :     char       *ptr;
     155              : 
     156          279 :     procStruct = (Form_pg_proc) GETSTRUCT(procTup);
     157          558 :     rv = snprintf(procName, sizeof(procName),
     158              :                   "__plpython_procedure_%s_%u",
     159          279 :                   NameStr(procStruct->proname),
     160              :                   fn_oid);
     161          279 :     if (rv >= sizeof(procName) || rv < 0)
     162            0 :         elog(ERROR, "procedure name would overrun buffer");
     163              : 
     164              :     /* Replace any not-legal-in-Python-names characters with '_' */
     165        13067 :     for (ptr = procName; *ptr; ptr++)
     166              :     {
     167        12788 :         if (!((*ptr >= 'A' && *ptr <= 'Z') ||
     168        12787 :               (*ptr >= 'a' && *ptr <= 'z') ||
     169         3427 :               (*ptr >= '0' && *ptr <= '9')))
     170         1930 :             *ptr = '_';
     171              :     }
     172              : 
     173              :     /* Create long-lived context that all procedure info will live in */
     174          279 :     cxt = AllocSetContextCreate(TopMemoryContext,
     175              :                                 "PL/Python function",
     176              :                                 ALLOCSET_DEFAULT_SIZES);
     177              : 
     178          279 :     oldcxt = MemoryContextSwitchTo(cxt);
     179              : 
     180          279 :     proc = palloc0_object(PLyProcedure);
     181          279 :     proc->mcxt = cxt;
     182              : 
     183          279 :     PG_TRY();
     184              :     {
     185              :         Datum       protrftypes_datum;
     186              :         Datum       prosrcdatum;
     187              :         bool        isnull;
     188              :         char       *procSource;
     189              :         int         i;
     190              : 
     191          279 :         proc->proname = pstrdup(NameStr(procStruct->proname));
     192          279 :         MemoryContextSetIdentifier(cxt, proc->proname);
     193          279 :         proc->pyname = pstrdup(procName);
     194          279 :         proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
     195          279 :         proc->fn_tid = procTup->t_self;
     196          279 :         proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
     197          279 :         proc->is_setof = procStruct->proretset;
     198          279 :         proc->is_procedure = (procStruct->prokind == PROKIND_PROCEDURE);
     199          279 :         proc->is_trigger = is_trigger;
     200          279 :         proc->src = NULL;
     201          279 :         proc->argnames = NULL;
     202          279 :         proc->args = NULL;
     203          279 :         proc->nargs = 0;
     204          279 :         proc->langid = procStruct->prolang;
     205          279 :         protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
     206              :                                             Anum_pg_proc_protrftypes,
     207              :                                             &isnull);
     208          279 :         proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
     209          279 :         proc->code = NULL;
     210          279 :         proc->statics = NULL;
     211          279 :         proc->globals = NULL;
     212          279 :         proc->calldepth = 0;
     213          279 :         proc->argstack = NULL;
     214              : 
     215              :         /*
     216              :          * get information required for output conversion of the return value,
     217              :          * but only if this isn't a trigger.
     218              :          */
     219          279 :         if (is_trigger == PLPY_NOT_TRIGGER)
     220              :         {
     221          228 :             Oid         rettype = procStruct->prorettype;
     222              :             HeapTuple   rvTypeTup;
     223              :             Form_pg_type rvTypeStruct;
     224              : 
     225          228 :             rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
     226          228 :             if (!HeapTupleIsValid(rvTypeTup))
     227            0 :                 elog(ERROR, "cache lookup failed for type %u", rettype);
     228          228 :             rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
     229              : 
     230              :             /* Disallow pseudotype result, except for void or record */
     231          228 :             if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
     232              :             {
     233           52 :                 if (rettype == VOIDOID ||
     234              :                     rettype == RECORDOID)
     235              :                      /* okay */ ;
     236            1 :                 else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
     237            1 :                     ereport(ERROR,
     238              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     239              :                              errmsg("trigger functions can only be called as triggers")));
     240              :                 else
     241            0 :                     ereport(ERROR,
     242              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     243              :                              errmsg("PL/Python functions cannot return type %s",
     244              :                                     format_type_be(rettype))));
     245              :             }
     246              : 
     247              :             /* set up output function for procedure result */
     248          227 :             PLy_output_setup_func(&proc->result, proc->mcxt,
     249              :                                   rettype, -1, proc);
     250              : 
     251          227 :             ReleaseSysCache(rvTypeTup);
     252              :         }
     253              :         else
     254              :         {
     255              :             /*
     256              :              * In a trigger function, we use proc->result and proc->result_in
     257              :              * for converting tuples, but we don't yet have enough info to set
     258              :              * them up.  PLy_exec_trigger will deal with it.
     259              :              */
     260           51 :             proc->result.typoid = InvalidOid;
     261           51 :             proc->result_in.typoid = InvalidOid;
     262              :         }
     263              : 
     264              :         /*
     265              :          * Now get information required for input conversion of the
     266              :          * procedure's arguments.  Note that we ignore output arguments here.
     267              :          * If the function returns record, those I/O functions will be set up
     268              :          * when the function is first called.
     269              :          */
     270          278 :         if (procStruct->pronargs)
     271              :         {
     272              :             Oid        *types;
     273              :             char      **names,
     274              :                        *modes;
     275              :             int         pos,
     276              :                         total;
     277              : 
     278              :             /* extract argument type info from the pg_proc tuple */
     279          107 :             total = get_func_arg_info(procTup, &types, &names, &modes);
     280              : 
     281              :             /* count number of in+inout args into proc->nargs */
     282          107 :             if (modes == NULL)
     283           96 :                 proc->nargs = total;
     284              :             else
     285              :             {
     286              :                 /* proc->nargs was initialized to 0 above */
     287           41 :                 for (i = 0; i < total; i++)
     288              :                 {
     289           30 :                     if (modes[i] != PROARGMODE_OUT &&
     290           18 :                         modes[i] != PROARGMODE_TABLE)
     291           18 :                         (proc->nargs)++;
     292              :                 }
     293              :             }
     294              : 
     295              :             /* Allocate arrays for per-input-argument data */
     296          107 :             proc->argnames = (char **) palloc0_array(char *, proc->nargs);
     297          107 :             proc->args = (PLyDatumToOb *) palloc0_array(PLyDatumToOb, proc->nargs);
     298              : 
     299          277 :             for (i = pos = 0; i < total; i++)
     300              :             {
     301              :                 HeapTuple   argTypeTup;
     302              :                 Form_pg_type argTypeStruct;
     303              : 
     304          170 :                 if (modes &&
     305           30 :                     (modes[i] == PROARGMODE_OUT ||
     306           18 :                      modes[i] == PROARGMODE_TABLE))
     307           12 :                     continue;   /* skip OUT arguments */
     308              : 
     309              :                 Assert(types[i] == procStruct->proargtypes.values[pos]);
     310              : 
     311          158 :                 argTypeTup = SearchSysCache1(TYPEOID,
     312          158 :                                              ObjectIdGetDatum(types[i]));
     313          158 :                 if (!HeapTupleIsValid(argTypeTup))
     314            0 :                     elog(ERROR, "cache lookup failed for type %u", types[i]);
     315          158 :                 argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
     316              : 
     317              :                 /* disallow pseudotype arguments */
     318          158 :                 if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
     319            0 :                     ereport(ERROR,
     320              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     321              :                              errmsg("PL/Python functions cannot accept type %s",
     322              :                                     format_type_be(types[i]))));
     323              : 
     324              :                 /* set up I/O function info */
     325          158 :                 PLy_input_setup_func(&proc->args[pos], proc->mcxt,
     326          158 :                                      types[i], -1,  /* typmod not known */
     327              :                                      proc);
     328              : 
     329              :                 /* get argument name */
     330          158 :                 proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;
     331              : 
     332          158 :                 ReleaseSysCache(argTypeTup);
     333              : 
     334          158 :                 pos++;
     335              :             }
     336              :         }
     337              : 
     338              :         /*
     339              :          * get the text of the function.
     340              :          */
     341          278 :         prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup,
     342              :                                              Anum_pg_proc_prosrc);
     343          278 :         procSource = TextDatumGetCString(prosrcdatum);
     344              : 
     345          278 :         PLy_procedure_compile(proc, procSource);
     346              : 
     347          275 :         pfree(procSource);
     348              :     }
     349            4 :     PG_CATCH();
     350              :     {
     351            4 :         MemoryContextSwitchTo(oldcxt);
     352            4 :         PLy_procedure_delete(proc);
     353            4 :         PG_RE_THROW();
     354              :     }
     355          275 :     PG_END_TRY();
     356              : 
     357          275 :     MemoryContextSwitchTo(oldcxt);
     358          275 :     return proc;
     359              : }
     360              : 
     361              : /*
     362              :  * Insert the procedure into the Python interpreter
     363              :  */
     364              : void
     365          299 : PLy_procedure_compile(PLyProcedure *proc, const char *src)
     366              : {
     367          299 :     PyObject   *crv = NULL;
     368              :     char       *msrc;
     369              :     PyObject   *code0;
     370              : 
     371          299 :     proc->globals = PyDict_Copy(PLy_interp_globals);
     372              : 
     373              :     /*
     374              :      * SD is private preserved data between calls. GD is global data shared by
     375              :      * all functions
     376              :      */
     377          299 :     proc->statics = PyDict_New();
     378          299 :     if (!proc->statics)
     379            0 :         PLy_elog(ERROR, NULL);
     380          299 :     PyDict_SetItemString(proc->globals, "SD", proc->statics);
     381              : 
     382              :     /*
     383              :      * insert the function code into the interpreter
     384              :      */
     385          299 :     msrc = PLy_procedure_munge_source(proc->pyname, src);
     386              :     /* Save the mangled source for later inclusion in tracebacks */
     387          299 :     proc->src = MemoryContextStrdup(proc->mcxt, msrc);
     388          299 :     code0 = Py_CompileString(msrc, "<string>", Py_file_input);
     389          299 :     if (code0)
     390          296 :         crv = PyEval_EvalCode(code0, proc->globals, NULL);
     391          299 :     pfree(msrc);
     392              : 
     393          299 :     if (crv != NULL)
     394              :     {
     395              :         int         clen;
     396              :         char        call[NAMEDATALEN + 256];
     397              : 
     398              :         Py_DECREF(crv);
     399              : 
     400              :         /*
     401              :          * compile a call to the function
     402              :          */
     403          296 :         clen = snprintf(call, sizeof(call), "%s()", proc->pyname);
     404          296 :         if (clen < 0 || clen >= sizeof(call))
     405            0 :             elog(ERROR, "string would overflow buffer");
     406          296 :         proc->code = Py_CompileString(call, "<string>", Py_eval_input);
     407          296 :         if (proc->code != NULL)
     408          296 :             return;
     409              :     }
     410              : 
     411            3 :     if (proc->proname)
     412            3 :         PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
     413              :                  proc->proname);
     414              :     else
     415            0 :         PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
     416              : }
     417              : 
     418              : void
     419           54 : PLy_procedure_delete(PLyProcedure *proc)
     420              : {
     421           54 :     Py_XDECREF(proc->code);
     422           54 :     Py_XDECREF(proc->statics);
     423           54 :     Py_XDECREF(proc->globals);
     424           54 :     MemoryContextDelete(proc->mcxt);
     425           54 : }
     426              : 
     427              : /*
     428              :  * Decide whether a cached PLyProcedure struct is still valid
     429              :  */
     430              : static bool
     431          677 : PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
     432              : {
     433          677 :     if (proc == NULL)
     434            0 :         return false;
     435              : 
     436              :     /* If the pg_proc tuple has changed, it's not valid */
     437         1349 :     if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
     438          672 :           ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
     439            5 :         return false;
     440              : 
     441          672 :     return true;
     442              : }
     443              : 
     444              : static char *
     445          299 : PLy_procedure_munge_source(const char *name, const char *src)
     446              : {
     447              :     char       *mrc,
     448              :                *mp;
     449              :     const char *sp;
     450              :     size_t      mlen;
     451              :     int         plen;
     452              : 
     453              :     /*
     454              :      * room for function source and the def statement
     455              :      */
     456          299 :     mlen = (strlen(src) * 2) + strlen(name) + 16;
     457              : 
     458          299 :     mrc = palloc(mlen);
     459          299 :     plen = snprintf(mrc, mlen, "def %s():\n\t", name);
     460              :     Assert(plen >= 0 && plen < mlen);
     461              : 
     462          299 :     sp = src;
     463          299 :     mp = mrc + plen;
     464              : 
     465        32181 :     while (*sp != '\0')
     466              :     {
     467        31882 :         if (*sp == '\r' && *(sp + 1) == '\n')
     468            3 :             sp++;
     469              : 
     470        31882 :         if (*sp == '\n' || *sp == '\r')
     471              :         {
     472         1346 :             *mp++ = '\n';
     473         1346 :             *mp++ = '\t';
     474         1346 :             sp++;
     475              :         }
     476              :         else
     477        30536 :             *mp++ = *sp++;
     478              :     }
     479          299 :     *mp++ = '\n';
     480          299 :     *mp++ = '\n';
     481          299 :     *mp = '\0';
     482              : 
     483          299 :     if (mp > (mrc + mlen))
     484            0 :         elog(FATAL, "buffer overrun in PLy_procedure_munge_source");
     485              : 
     486          299 :     return mrc;
     487              : }
        

Generated by: LCOV version 2.0-1