LCOV - differential code coverage report
Current view: top level - src/pl/plpython - plpy_procedure.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 90.3 % 186 168 3 3 12 3 27 1 137 3 26 2
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 4 1 3 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a