LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_main.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19beta1 Lines: 94.1 % 169 159
Test Date: 2026-06-24 17:16:41 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1