LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_main.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 93.0 % 186 173
Test Date: 2026-11-15 00:16:11 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.5 % 179 119

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * PL/Python main entry points
       3                 :             :  *
       4                 :             :  * src/pl/plpython/plpy_main.c
       5                 :             :  */
       6                 :             : 
       7                 :             : #include "postgres.h"
       8                 :             : 
       9                 :             : #include "catalog/pg_proc.h"
      10                 :             : #include "commands/event_trigger.h"
      11                 :             : #include "commands/trigger.h"
      12                 :             : #include "executor/spi.h"
      13                 :             : #include "funcapi.h"
      14                 :             : #include "miscadmin.h"
      15                 :             : #include "plpy_elog.h"
      16                 :             : #include "plpy_exec.h"
      17                 :             : #include "plpy_main.h"
      18                 :             : #include "plpy_plpymodule.h"
      19                 :             : #include "plpy_subxactobject.h"
      20                 :             : #include "plpy_util.h"
      21                 :             : #include "utils/builtins.h"
      22                 :             : #include "utils/guc.h"
      23                 :             : #include "utils/lsyscache.h"
      24                 :             : #include "utils/memutils.h"
      25                 :             : #include "utils/syscache.h"
      26                 :             : 
      27                 :             : /*
      28                 :             :  * exported functions
      29                 :             :  */
      30                 :             : 
      31                 :          23 : PG_MODULE_MAGIC_EXT(
      32                 :             :                     .name = "plpython",
      33                 :             :                     .version = PG_VERSION
      34                 :             : );
      35                 :             : 
      36                 :          26 : PG_FUNCTION_INFO_V1(plpython3_validator);
      37                 :          26 : PG_FUNCTION_INFO_V1(plpython3_call_handler);
      38                 :           8 : PG_FUNCTION_INFO_V1(plpython3_inline_handler);
      39                 :             : 
      40                 :             : 
      41                 :             : static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct);
      42                 :             : static void plpython_error_callback(void *arg);
      43                 :             : static void plpython_inline_error_callback(void *arg);
      44                 :             : 
      45                 :             : static PLyExecutionContext *PLy_push_execution_context(bool atomic_context);
      46                 :             : static void PLy_pop_execution_context(void);
      47                 :             : 
      48                 :             : /* initialize global variables */
      49                 :             : PyObject   *PLy_interp_globals = NULL;
      50                 :             : 
      51                 :             : /* this doesn't need to be global; use PLy_current_execution_context() */
      52                 :             : static PLyExecutionContext *PLy_execution_contexts = NULL;
      53                 :             : 
      54                 :             : 
      55                 :             : void
      56                 :          23 : _PG_init(void)
      57                 :             : {
      58                 :             :     PyObject   *main_mod;
      59                 :             :     PyObject   *main_dict;
      60                 :             :     PyObject   *GD;
      61                 :             :     PyObject   *plpy_mod;
      62                 :             : 
      63                 :          23 :     pg_bindtextdomain(TEXTDOMAIN);
      64                 :             : 
      65                 :             :     /* Add plpy to table of built-in modules. */
      66                 :          23 :     PyImport_AppendInittab("plpy", PyInit_plpy);
      67                 :             : 
      68                 :             :     /* Initialize Python interpreter. */
      69                 :          23 :     Py_Initialize();
      70                 :             : 
      71                 :          23 :     main_mod = PyImport_AddModule("__main__");
      72   [ +  -  -  + ]:          23 :     if (main_mod == NULL || PyErr_Occurred())
      73                 :           0 :         PLy_elog(ERROR, "could not import \"%s\" module", "__main__");
      74                 :             :     Py_INCREF(main_mod);
      75                 :             : 
      76                 :          23 :     main_dict = PyModule_GetDict(main_mod);
      77         [ -  + ]:          23 :     if (main_dict == NULL)
      78                 :           0 :         PLy_elog(ERROR, NULL);
      79                 :             : 
      80                 :             :     /*
      81                 :             :      * Set up GD.
      82                 :             :      */
      83                 :          23 :     GD = PyDict_New();
      84         [ -  + ]:          23 :     if (GD == NULL)
      85                 :           0 :         PLy_elog(ERROR, NULL);
      86                 :          23 :     PyDict_SetItemString(main_dict, "GD", GD);
      87                 :             : 
      88                 :             :     /*
      89                 :             :      * Import plpy.
      90                 :             :      */
      91                 :          23 :     plpy_mod = PyImport_ImportModule("plpy");
      92         [ -  + ]:          23 :     if (plpy_mod == NULL)
      93                 :           0 :         PLy_elog(ERROR, "could not import \"%s\" module", "plpy");
      94         [ -  + ]:          23 :     if (PyDict_SetItemString(main_dict, "plpy", plpy_mod) == -1)
      95                 :           0 :         PLy_elog(ERROR, NULL);
      96                 :             : 
      97         [ -  + ]:          23 :     if (PyErr_Occurred())
      98                 :           0 :         PLy_elog(FATAL, "untrapped error in initialization");
      99                 :             : 
     100                 :             :     Py_INCREF(main_dict);
     101                 :          23 :     PLy_interp_globals = main_dict;
     102                 :             : 
     103                 :             :     Py_DECREF(main_mod);
     104                 :             : 
     105                 :          23 :     explicit_subtransactions = NIL;
     106                 :             : 
     107                 :          23 :     PLy_execution_contexts = NULL;
     108                 :          23 : }
     109                 :             : 
     110                 :             : Datum
     111                 :         274 : plpython3_validator(PG_FUNCTION_ARGS)
     112                 :             : {
     113                 :         274 :     LOCAL_FCINFO(fake_fcinfo, 0);
     114                 :         274 :     Oid         funcoid = PG_GETARG_OID(0);
     115                 :             :     HeapTuple   tuple;
     116                 :             :     Form_pg_proc procStruct;
     117                 :             :     Oid         rettype;
     118                 :             :     Oid        *argtypes;
     119                 :             :     char      **argnames,
     120                 :             :                *argmodes;
     121                 :             :     int         nargs;
     122                 :             :     PLyTrigType is_trigger;
     123                 :             :     TriggerData trigdata;
     124                 :             :     EventTriggerData etrigdata;
     125                 :             :     FmgrInfo    flinfo;
     126                 :             :     PLyProcedureCache *pcache;
     127                 :             : 
     128         [ -  + ]:         274 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     129                 :           0 :         PG_RETURN_VOID();
     130                 :             : 
     131                 :             :     /* Get the new function's pg_proc entry */
     132                 :         274 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     133         [ -  + ]:         274 :     if (!HeapTupleIsValid(tuple))
     134         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     135                 :         274 :     procStruct = (Form_pg_proc) GETSTRUCT(tuple);
     136                 :             : 
     137                 :             :     /* Disallow pseudotype result, except for void or record */
     138                 :         274 :     rettype = procStruct->prorettype;
     139         [ +  + ]:         274 :     if (get_typtype(rettype) == TYPTYPE_PSEUDO)
     140                 :             :     {
     141   [ +  +  +  + ]:          83 :         if (rettype == VOIDOID || rettype == RECORDOID)
     142                 :             :              /* okay */ ;
     143   [ +  +  +  + ]:          26 :         else if (rettype == TRIGGEROID || rettype == EVENT_TRIGGEROID)
     144                 :             :              /* okay */ ;
     145                 :             :         else
     146         [ +  - ]:           1 :             ereport(ERROR,
     147                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     148                 :             :                      errmsg("PL/Python functions cannot return type %s",
     149                 :             :                             format_type_be(rettype))));
     150                 :             :     }
     151                 :             : 
     152                 :             :     /* Disallow pseudotype arguments, except for output records */
     153                 :         273 :     nargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes);
     154         [ +  + ]:         466 :     for (int i = 0; i < nargs; i++)
     155                 :             :     {
     156         [ +  + ]:         194 :         if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
     157                 :             :         {
     158         [ +  + ]:           3 :             if (argmodes &&
     159         [ -  + ]:           2 :                 (argmodes[i] == PROARGMODE_OUT ||
     160         [ #  # ]:           0 :                  argmodes[i] == PROARGMODE_TABLE))
     161                 :             :             {
     162         [ -  + ]:           2 :                 if (rettype == RECORDOID)
     163                 :             :                      /* okay */ ;
     164                 :             :                 else
     165         [ #  # ]:           0 :                     ereport(ERROR,
     166                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     167                 :             :                              errmsg("PL/Python functions cannot return type %s",
     168                 :             :                                     format_type_be(argtypes[i]))));
     169                 :             :             }
     170                 :             :             else
     171         [ +  - ]:           1 :                 ereport(ERROR,
     172                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     173                 :             :                          errmsg("PL/Python functions cannot accept type %s",
     174                 :             :                                 format_type_be(argtypes[i]))));
     175                 :             :         }
     176                 :             :     }
     177                 :             : 
     178                 :         272 :     is_trigger = PLy_procedure_is_trigger(procStruct);
     179                 :             : 
     180                 :         272 :     ReleaseSysCache(tuple);
     181                 :             : 
     182                 :             :     /* The rest is not needed/wanted if check_function_bodies is off */
     183         [ +  + ]:         272 :     if (!check_function_bodies)
     184                 :           1 :         PG_RETURN_VOID();
     185                 :             : 
     186                 :             :     /*
     187                 :             :      * Set up a fake flinfo/fcinfo with just enough info to satisfy
     188                 :             :      * PLy_procedure_get().  That function derives the call context (plain
     189                 :             :      * function, DML trigger, or event trigger) from the fcinfo, so we have to
     190                 :             :      * construct matching context here.
     191                 :             :      */
     192   [ +  -  +  -  :        1355 :     MemSet(fake_fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  +  -  +  
                      + ]
     193   [ +  -  +  -  :        1897 :     MemSet(&flinfo, 0, sizeof(flinfo));
          +  -  +  -  +  
                      + ]
     194                 :         271 :     fake_fcinfo->flinfo = &flinfo;
     195                 :         271 :     flinfo.fn_oid = funcoid;
     196                 :         271 :     flinfo.fn_mcxt = CurrentMemoryContext;
     197                 :             : 
     198         [ +  + ]:         271 :     if (is_trigger == PLPY_TRIGGER)
     199                 :             :     {
     200   [ +  -  +  -  :         264 :         MemSet(&trigdata, 0, sizeof(trigdata));
          +  -  +  -  +  
                      + ]
     201                 :          24 :         trigdata.type = T_TriggerData;
     202                 :             :         /* We can't validate triggers against any particular table ... */
     203                 :          24 :         fake_fcinfo->context = (Node *) &trigdata;
     204                 :             :     }
     205         [ +  + ]:         247 :     else if (is_trigger == PLPY_EVENT_TRIGGER)
     206                 :             :     {
     207   [ +  -  +  -  :           5 :         MemSet(&etrigdata, 0, sizeof(etrigdata));
          +  -  +  -  +  
                      + ]
     208                 :           1 :         etrigdata.type = T_EventTriggerData;
     209                 :           1 :         fake_fcinfo->context = (Node *) &etrigdata;
     210                 :             :     }
     211                 :             : 
     212                 :         271 :     pcache = PLy_procedure_get(fake_fcinfo, true);
     213                 :             : 
     214                 :             :     /*
     215                 :             :      * Release the reference count that PLy_procedure_get acquired; the
     216                 :             :      * PLyProcedure object remains valid for possible future use.  (We could
     217                 :             :      * leave this to be done when the calling memory context is cleaned up,
     218                 :             :      * but it seems neater to do it right away.  Note we mustn't release the
     219                 :             :      * pcache object, since the memory-context reset callback has a reference
     220                 :             :      * to it.)
     221                 :             :      */
     222                 :             :     Assert(pcache->proc->cfunc.use_count > 0);
     223                 :         270 :     pcache->proc->cfunc.use_count--;
     224                 :         270 :     pcache->proc = NULL;
     225                 :             : 
     226                 :         270 :     PG_RETURN_VOID();
     227                 :             : }
     228                 :             : 
     229                 :             : Datum
     230                 :         734 : plpython3_call_handler(PG_FUNCTION_ARGS)
     231                 :             : {
     232                 :             :     bool        nonatomic;
     233                 :             :     Datum       retval;
     234                 :             :     PLyExecutionContext *exec_ctx;
     235                 :             :     ErrorContextCallback plerrcontext;
     236                 :             : 
     237                 :        1535 :     nonatomic = fcinfo->context &&
     238   [ +  +  +  + ]:         742 :         IsA(fcinfo->context, CallContext) &&
     239         [ +  + ]:           8 :         !castNode(CallContext, fcinfo->context)->atomic;
     240                 :             : 
     241                 :             :     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
     242                 :         734 :     SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
     243                 :             : 
     244                 :             :     /*
     245                 :             :      * Push execution context onto stack.  It is important that this get
     246                 :             :      * popped again, so avoid putting anything that could throw error between
     247                 :             :      * here and the PG_TRY.
     248                 :             :      */
     249                 :         734 :     exec_ctx = PLy_push_execution_context(!nonatomic);
     250                 :             : 
     251         [ +  + ]:         734 :     PG_TRY();
     252                 :             :     {
     253                 :             :         PLyProcedureCache *pcache;
     254                 :             : 
     255                 :             :         /*
     256                 :             :          * Setup error traceback support for ereport().  Note that the PG_TRY
     257                 :             :          * structure pops this for us again at exit, so we needn't do that
     258                 :             :          * explicitly, nor do we risk the callback getting called after we've
     259                 :             :          * destroyed the exec_ctx.
     260                 :             :          */
     261                 :         734 :         plerrcontext.callback = plpython_error_callback;
     262                 :         734 :         plerrcontext.arg = exec_ctx;
     263                 :         734 :         plerrcontext.previous = error_context_stack;
     264                 :         734 :         error_context_stack = &plerrcontext;
     265                 :             : 
     266                 :             :         /*
     267                 :             :          * Look up (and if necessary compile) the procedure.  This can throw
     268                 :             :          * an error, so it must happen inside the PG_TRY so that the execution
     269                 :             :          * context gets popped on the way out.
     270                 :             :          */
     271                 :         734 :         pcache = PLy_procedure_get(fcinfo, false);
     272                 :         731 :         exec_ctx->curr_proc = pcache->proc;
     273                 :             : 
     274   [ +  +  +  + ]:         731 :         if (CALLED_AS_TRIGGER(fcinfo))
     275                 :          40 :         {
     276                 :             :             HeapTuple   trv;
     277                 :             : 
     278                 :          49 :             trv = PLy_exec_trigger(fcinfo, pcache->proc);
     279                 :          40 :             retval = PointerGetDatum(trv);
     280                 :             :         }
     281   [ +  +  +  + ]:         682 :         else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
     282                 :             :         {
     283                 :          10 :             PLy_exec_event_trigger(fcinfo, pcache->proc);
     284                 :          10 :             retval = (Datum) 0;
     285                 :             :         }
     286                 :             :         else
     287                 :         672 :             retval = PLy_exec_function(fcinfo, pcache);
     288                 :             :     }
     289                 :         108 :     PG_CATCH();
     290                 :             :     {
     291                 :             :         /* Destroy the execution context */
     292                 :         108 :         PLy_pop_execution_context();
     293                 :         108 :         PyErr_Clear();
     294                 :             : 
     295                 :         108 :         PG_RE_THROW();
     296                 :             :     }
     297         [ -  + ]:         626 :     PG_END_TRY();
     298                 :             : 
     299                 :             :     /* Destroy the execution context */
     300                 :         626 :     PLy_pop_execution_context();
     301                 :             : 
     302                 :         626 :     return retval;
     303                 :             : }
     304                 :             : 
     305                 :             : Datum
     306                 :          21 : plpython3_inline_handler(PG_FUNCTION_ARGS)
     307                 :             : {
     308                 :          21 :     LOCAL_FCINFO(fake_fcinfo, 0);
     309                 :          21 :     InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
     310                 :             :     FmgrInfo    flinfo;
     311                 :             :     PLyProcedure proc;
     312                 :             :     PLyProcedureCache pcache;
     313                 :             :     PLyExecutionContext *exec_ctx;
     314                 :             :     ErrorContextCallback plerrcontext;
     315                 :             : 
     316                 :             :     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
     317                 :          21 :     SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
     318                 :             : 
     319   [ +  -  +  -  :         105 :     MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  +  -  +  
                      + ]
     320   [ +  -  +  -  :         147 :     MemSet(&flinfo, 0, sizeof(flinfo));
          +  -  +  -  +  
                      + ]
     321                 :          21 :     fake_fcinfo->flinfo = &flinfo;
     322                 :          21 :     flinfo.fn_oid = InvalidOid;
     323                 :          21 :     flinfo.fn_mcxt = CurrentMemoryContext;
     324                 :             : 
     325   [ +  -  +  -  :         945 :     MemSet(&proc, 0, sizeof(PLyProcedure));
          +  -  +  -  +  
                      + ]
     326                 :          21 :     proc.mcxt = AllocSetContextCreate(TopMemoryContext,
     327                 :             :                                       "__plpython_inline_block",
     328                 :             :                                       ALLOCSET_DEFAULT_SIZES);
     329                 :          21 :     proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
     330                 :          21 :     proc.langid = codeblock->langOid;
     331                 :             : 
     332                 :             :     /*
     333                 :             :      * This is currently sufficient to get PLy_exec_function to work, but
     334                 :             :      * someday we might need to be honest and use PLy_output_setup_func.
     335                 :             :      */
     336                 :          21 :     proc.result.typoid = VOIDOID;
     337                 :             : 
     338                 :             :     /* Set up a minimal PLyProcedureCache for the inline block */
     339   [ +  -  +  -  :         168 :     MemSet(&pcache, 0, sizeof(PLyProcedureCache));
          +  -  +  -  +  
                      + ]
     340                 :          21 :     pcache.proc = &proc;
     341                 :          21 :     pcache.fcontext = CurrentMemoryContext;
     342                 :             : 
     343                 :             :     /*
     344                 :             :      * Push execution context onto stack.  It is important that this get
     345                 :             :      * popped again, so avoid putting anything that could throw error between
     346                 :             :      * here and the PG_TRY.
     347                 :             :      */
     348                 :          21 :     exec_ctx = PLy_push_execution_context(codeblock->atomic);
     349                 :             : 
     350         [ +  + ]:          21 :     PG_TRY();
     351                 :             :     {
     352                 :             :         /*
     353                 :             :          * Setup error traceback support for ereport().
     354                 :             :          * plpython_inline_error_callback doesn't currently need exec_ctx, but
     355                 :             :          * for consistency with plpython3_call_handler we do it the same way.
     356                 :             :          */
     357                 :          21 :         plerrcontext.callback = plpython_inline_error_callback;
     358                 :          21 :         plerrcontext.arg = exec_ctx;
     359                 :          21 :         plerrcontext.previous = error_context_stack;
     360                 :          21 :         error_context_stack = &plerrcontext;
     361                 :             : 
     362                 :             :         /* An inline block has its own SD, used for this call only */
     363                 :          21 :         proc.statics = PyDict_New();
     364         [ -  + ]:          21 :         if (!proc.statics)
     365                 :           0 :             PLy_elog(ERROR, NULL);
     366                 :             : 
     367                 :          21 :         PLy_procedure_compile(&proc, codeblock->source_text);
     368                 :          21 :         exec_ctx->curr_proc = &proc;
     369                 :          21 :         PLy_exec_function(fake_fcinfo, &pcache);
     370                 :             :     }
     371                 :          11 :     PG_CATCH();
     372                 :             :     {
     373                 :          11 :         PLy_pop_execution_context();
     374                 :          11 :         PLy_procedure_delete(&proc);
     375                 :          11 :         PyErr_Clear();
     376                 :          11 :         PG_RE_THROW();
     377                 :             :     }
     378         [ -  + ]:          10 :     PG_END_TRY();
     379                 :             : 
     380                 :             :     /* Destroy the execution context */
     381                 :          10 :     PLy_pop_execution_context();
     382                 :             : 
     383                 :             :     /* Now clean up the transient procedure we made */
     384                 :          10 :     PLy_procedure_delete(&proc);
     385                 :             : 
     386                 :          10 :     PG_RETURN_VOID();
     387                 :             : }
     388                 :             : 
     389                 :             : /*
     390                 :             :  * Determine whether a function is a (DML or event) trigger from its pg_proc
     391                 :             :  * result type.  This is used by the validator, which has no call context to
     392                 :             :  * inspect; the call handler instead relies on the fcinfo's call context.
     393                 :             :  */
     394                 :             : static PLyTrigType
     395                 :         272 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
     396                 :             : {
     397                 :             :     PLyTrigType ret;
     398                 :             : 
     399      [ +  +  + ]:         272 :     switch (procStruct->prorettype)
     400                 :             :     {
     401                 :          24 :         case TRIGGEROID:
     402                 :          24 :             ret = PLPY_TRIGGER;
     403                 :          24 :             break;
     404                 :           1 :         case EVENT_TRIGGEROID:
     405                 :           1 :             ret = PLPY_EVENT_TRIGGER;
     406                 :           1 :             break;
     407                 :         247 :         default:
     408                 :         247 :             ret = PLPY_NOT_TRIGGER;
     409                 :         247 :             break;
     410                 :             :     }
     411                 :             : 
     412                 :         272 :     return ret;
     413                 :             : }
     414                 :             : 
     415                 :             : static void
     416                 :         484 : plpython_error_callback(void *arg)
     417                 :             : {
     418                 :         484 :     PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
     419                 :             : 
     420         [ +  + ]:         484 :     if (exec_ctx->curr_proc)
     421                 :             :     {
     422         [ +  + ]:         481 :         if (exec_ctx->curr_proc->is_procedure)
     423                 :           4 :             errcontext("PL/Python procedure \"%s\"",
     424                 :             :                        PLy_procedure_name(exec_ctx->curr_proc));
     425                 :             :         else
     426                 :         477 :             errcontext("PL/Python function \"%s\"",
     427                 :             :                        PLy_procedure_name(exec_ctx->curr_proc));
     428                 :             :     }
     429                 :         484 : }
     430                 :             : 
     431                 :             : static void
     432                 :          28 : plpython_inline_error_callback(void *arg)
     433                 :             : {
     434                 :          28 :     errcontext("PL/Python anonymous code block");
     435                 :          28 : }
     436                 :             : 
     437                 :             : PLyExecutionContext *
     438                 :        1469 : PLy_current_execution_context(void)
     439                 :             : {
     440         [ -  + ]:        1469 :     if (PLy_execution_contexts == NULL)
     441         [ #  # ]:           0 :         elog(ERROR, "no Python function is currently executing");
     442                 :             : 
     443                 :        1469 :     return PLy_execution_contexts;
     444                 :             : }
     445                 :             : 
     446                 :             : MemoryContext
     447                 :         807 : PLy_get_scratch_context(PLyExecutionContext *context)
     448                 :             : {
     449                 :             :     /*
     450                 :             :      * A scratch context might never be needed in a given plpython procedure,
     451                 :             :      * so allocate it on first request.
     452                 :             :      */
     453         [ +  + ]:         807 :     if (context->scratch_ctx == NULL)
     454                 :         450 :         context->scratch_ctx =
     455                 :         450 :             AllocSetContextCreate(TopTransactionContext,
     456                 :             :                                   "PL/Python scratch context",
     457                 :             :                                   ALLOCSET_DEFAULT_SIZES);
     458                 :         807 :     return context->scratch_ctx;
     459                 :             : }
     460                 :             : 
     461                 :             : static PLyExecutionContext *
     462                 :         755 : PLy_push_execution_context(bool atomic_context)
     463                 :             : {
     464                 :             :     PLyExecutionContext *context;
     465                 :             : 
     466                 :             :     /* Pick a memory context similar to what SPI uses. */
     467                 :             :     context = (PLyExecutionContext *)
     468         [ +  + ]:         755 :         MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
     469                 :             :                            sizeof(PLyExecutionContext));
     470                 :         755 :     context->curr_proc = NULL;
     471                 :         755 :     context->scratch_ctx = NULL;
     472                 :         755 :     context->next = PLy_execution_contexts;
     473                 :         755 :     PLy_execution_contexts = context;
     474                 :         755 :     return context;
     475                 :             : }
     476                 :             : 
     477                 :             : static void
     478                 :         755 : PLy_pop_execution_context(void)
     479                 :             : {
     480                 :         755 :     PLyExecutionContext *context = PLy_execution_contexts;
     481                 :             : 
     482         [ -  + ]:         755 :     if (context == NULL)
     483         [ #  # ]:           0 :         elog(ERROR, "no Python function is currently executing");
     484                 :             : 
     485                 :         755 :     PLy_execution_contexts = context->next;
     486                 :             : 
     487         [ +  + ]:         755 :     if (context->scratch_ctx)
     488                 :         433 :         MemoryContextDelete(context->scratch_ctx);
     489                 :         755 :     pfree(context);
     490                 :         755 : }
        

Generated by: LCOV version 2.0-1