LCOV - code coverage report
Current view: top level - src/pl/plpython - plpy_main.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 163 172 94.8 %
Date: 2025-09-10 21:18:40 Functions: 17 17 100.0 %
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 "access/htup_details.h"
      10             : #include "catalog/pg_proc.h"
      11             : #include "catalog/pg_type.h"
      12             : #include "commands/event_trigger.h"
      13             : #include "commands/trigger.h"
      14             : #include "executor/spi.h"
      15             : #include "miscadmin.h"
      16             : #include "plpy_elog.h"
      17             : #include "plpy_exec.h"
      18             : #include "plpy_main.h"
      19             : #include "plpy_plpymodule.h"
      20             : #include "plpy_procedure.h"
      21             : #include "plpy_subxactobject.h"
      22             : #include "plpy_util.h"
      23             : #include "utils/guc.h"
      24             : #include "utils/memutils.h"
      25             : #include "utils/rel.h"
      26             : #include "utils/syscache.h"
      27             : 
      28             : /*
      29             :  * exported functions
      30             :  */
      31             : 
      32          46 : PG_MODULE_MAGIC_EXT(
      33             :                     .name = "plpython",
      34             :                     .version = PG_VERSION
      35             : );
      36             : 
      37          52 : PG_FUNCTION_INFO_V1(plpython3_validator);
      38          52 : PG_FUNCTION_INFO_V1(plpython3_call_handler);
      39          16 : PG_FUNCTION_INFO_V1(plpython3_inline_handler);
      40             : 
      41             : 
      42             : static PLyTrigType PLy_procedure_is_trigger(Form_pg_proc procStruct);
      43             : static void plpython_error_callback(void *arg);
      44             : static void plpython_inline_error_callback(void *arg);
      45             : static void PLy_init_interp(void);
      46             : 
      47             : static PLyExecutionContext *PLy_push_execution_context(bool atomic_context);
      48             : static void PLy_pop_execution_context(void);
      49             : 
      50             : /* static state for Python library conflict detection */
      51             : static int *plpython_version_bitmask_ptr = NULL;
      52             : static int  plpython_version_bitmask = 0;
      53             : 
      54             : /* initialize global variables */
      55             : PyObject   *PLy_interp_globals = NULL;
      56             : 
      57             : /* this doesn't need to be global; use PLy_current_execution_context() */
      58             : static PLyExecutionContext *PLy_execution_contexts = NULL;
      59             : 
      60             : 
      61             : void
      62          46 : _PG_init(void)
      63             : {
      64             :     int       **bitmask_ptr;
      65             : 
      66             :     /*
      67             :      * Set up a shared bitmask variable telling which Python version(s) are
      68             :      * loaded into this process's address space.  If there's more than one, we
      69             :      * cannot call into libpython for fear of causing crashes.  But postpone
      70             :      * the actual failure for later, so that operations like pg_restore can
      71             :      * load more than one plpython library so long as they don't try to do
      72             :      * anything much with the language.
      73             :      *
      74             :      * While we only support Python 3 these days, somebody might create an
      75             :      * out-of-tree version adding back support for Python 2. Conflicts with
      76             :      * such an extension should be detected.
      77             :      */
      78          46 :     bitmask_ptr = (int **) find_rendezvous_variable("plpython_version_bitmask");
      79          46 :     if (!(*bitmask_ptr))        /* am I the first? */
      80          46 :         *bitmask_ptr = &plpython_version_bitmask;
      81             :     /* Retain pointer to the agreed-on shared variable ... */
      82          46 :     plpython_version_bitmask_ptr = *bitmask_ptr;
      83             :     /* ... and announce my presence */
      84          46 :     *plpython_version_bitmask_ptr |= (1 << PY_MAJOR_VERSION);
      85             : 
      86             :     /*
      87             :      * This should be safe even in the presence of conflicting plpythons, and
      88             :      * it's necessary to do it before possibly throwing a conflict error, or
      89             :      * the error message won't get localized.
      90             :      */
      91          46 :     pg_bindtextdomain(TEXTDOMAIN);
      92          46 : }
      93             : 
      94             : /*
      95             :  * Perform one-time setup of PL/Python, after checking for a conflict
      96             :  * with other versions of Python.
      97             :  */
      98             : static void
      99        1944 : PLy_initialize(void)
     100             : {
     101             :     static bool inited = false;
     102             : 
     103             :     /*
     104             :      * Check for multiple Python libraries before actively doing anything with
     105             :      * libpython.  This must be repeated on each entry to PL/Python, in case a
     106             :      * conflicting library got loaded since we last looked.
     107             :      *
     108             :      * It is attractive to weaken this error from FATAL to ERROR, but there
     109             :      * would be corner cases, so it seems best to be conservative.
     110             :      */
     111        1944 :     if (*plpython_version_bitmask_ptr != (1 << PY_MAJOR_VERSION))
     112           0 :         ereport(FATAL,
     113             :                 (errmsg("multiple Python libraries are present in session"),
     114             :                  errdetail("Only one Python major version can be used in one session.")));
     115             : 
     116             :     /* The rest should only be done once per session */
     117        1944 :     if (inited)
     118        1898 :         return;
     119             : 
     120          46 :     PyImport_AppendInittab("plpy", PyInit_plpy);
     121          46 :     Py_Initialize();
     122          46 :     PyImport_ImportModule("plpy");
     123          46 :     PLy_init_interp();
     124          46 :     PLy_init_plpy();
     125          46 :     if (PyErr_Occurred())
     126           0 :         PLy_elog(FATAL, "untrapped error in initialization");
     127             : 
     128          46 :     init_procedure_caches();
     129             : 
     130          46 :     explicit_subtransactions = NIL;
     131             : 
     132          46 :     PLy_execution_contexts = NULL;
     133             : 
     134          46 :     inited = true;
     135             : }
     136             : 
     137             : /*
     138             :  * This should be called only once, from PLy_initialize. Initialize the Python
     139             :  * interpreter and global data.
     140             :  */
     141             : static void
     142          46 : PLy_init_interp(void)
     143             : {
     144             :     static PyObject *PLy_interp_safe_globals = NULL;
     145             :     PyObject   *mainmod;
     146             : 
     147          46 :     mainmod = PyImport_AddModule("__main__");
     148          46 :     if (mainmod == NULL || PyErr_Occurred())
     149           0 :         PLy_elog(ERROR, "could not import \"__main__\" module");
     150          46 :     Py_INCREF(mainmod);
     151          46 :     PLy_interp_globals = PyModule_GetDict(mainmod);
     152          46 :     PLy_interp_safe_globals = PyDict_New();
     153          46 :     if (PLy_interp_safe_globals == NULL)
     154           0 :         PLy_elog(ERROR, NULL);
     155          46 :     PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
     156          46 :     Py_DECREF(mainmod);
     157          46 :     if (PLy_interp_globals == NULL || PyErr_Occurred())
     158           0 :         PLy_elog(ERROR, "could not initialize globals");
     159          46 : }
     160             : 
     161             : Datum
     162         502 : plpython3_validator(PG_FUNCTION_ARGS)
     163             : {
     164         502 :     Oid         funcoid = PG_GETARG_OID(0);
     165             :     HeapTuple   tuple;
     166             :     Form_pg_proc procStruct;
     167             :     PLyTrigType is_trigger;
     168             : 
     169         502 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     170           0 :         PG_RETURN_VOID();
     171             : 
     172         502 :     if (!check_function_bodies)
     173           2 :         PG_RETURN_VOID();
     174             : 
     175             :     /* Do this only after making sure we need to do something */
     176         500 :     PLy_initialize();
     177             : 
     178             :     /* Get the new function's pg_proc entry */
     179         500 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     180         500 :     if (!HeapTupleIsValid(tuple))
     181           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     182         500 :     procStruct = (Form_pg_proc) GETSTRUCT(tuple);
     183             : 
     184         500 :     is_trigger = PLy_procedure_is_trigger(procStruct);
     185             : 
     186         500 :     ReleaseSysCache(tuple);
     187             : 
     188             :     /* We can't validate triggers against any particular table ... */
     189         500 :     PLy_procedure_get(funcoid, InvalidOid, is_trigger);
     190             : 
     191         498 :     PG_RETURN_VOID();
     192             : }
     193             : 
     194             : Datum
     195        1402 : plpython3_call_handler(PG_FUNCTION_ARGS)
     196             : {
     197             :     bool        nonatomic;
     198             :     Datum       retval;
     199             :     PLyExecutionContext *exec_ctx;
     200             :     ErrorContextCallback plerrcontext;
     201             : 
     202        1402 :     PLy_initialize();
     203             : 
     204        2938 :     nonatomic = fcinfo->context &&
     205        1418 :         IsA(fcinfo->context, CallContext) &&
     206          16 :         !castNode(CallContext, fcinfo->context)->atomic;
     207             : 
     208             :     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
     209        1402 :     SPI_connect_ext(nonatomic ? SPI_OPT_NONATOMIC : 0);
     210             : 
     211             :     /*
     212             :      * Push execution context onto stack.  It is important that this get
     213             :      * popped again, so avoid putting anything that could throw error between
     214             :      * here and the PG_TRY.
     215             :      */
     216        1402 :     exec_ctx = PLy_push_execution_context(!nonatomic);
     217             : 
     218        1402 :     PG_TRY();
     219             :     {
     220        1402 :         Oid         funcoid = fcinfo->flinfo->fn_oid;
     221             :         PLyProcedure *proc;
     222             : 
     223             :         /*
     224             :          * Setup error traceback support for ereport().  Note that the PG_TRY
     225             :          * structure pops this for us again at exit, so we needn't do that
     226             :          * explicitly, nor do we risk the callback getting called after we've
     227             :          * destroyed the exec_ctx.
     228             :          */
     229        1402 :         plerrcontext.callback = plpython_error_callback;
     230        1402 :         plerrcontext.arg = exec_ctx;
     231        1402 :         plerrcontext.previous = error_context_stack;
     232        1402 :         error_context_stack = &plerrcontext;
     233             : 
     234        1402 :         if (CALLED_AS_TRIGGER(fcinfo))
     235          80 :         {
     236          98 :             Relation    tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
     237             :             HeapTuple   trv;
     238             : 
     239          98 :             proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), PLPY_TRIGGER);
     240          98 :             exec_ctx->curr_proc = proc;
     241          98 :             trv = PLy_exec_trigger(fcinfo, proc);
     242          80 :             retval = PointerGetDatum(trv);
     243             :         }
     244        1304 :         else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
     245             :         {
     246          20 :             proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_EVENT_TRIGGER);
     247          20 :             exec_ctx->curr_proc = proc;
     248          20 :             PLy_exec_event_trigger(fcinfo, proc);
     249          20 :             retval = (Datum) 0;
     250             :         }
     251             :         else
     252             :         {
     253        1284 :             proc = PLy_procedure_get(funcoid, InvalidOid, PLPY_NOT_TRIGGER);
     254        1278 :             exec_ctx->curr_proc = proc;
     255        1278 :             retval = PLy_exec_function(fcinfo, proc);
     256             :         }
     257             :     }
     258         186 :     PG_CATCH();
     259             :     {
     260         186 :         PLy_pop_execution_context();
     261         186 :         PyErr_Clear();
     262         186 :         PG_RE_THROW();
     263             :     }
     264        1216 :     PG_END_TRY();
     265             : 
     266             :     /* Destroy the execution context */
     267        1216 :     PLy_pop_execution_context();
     268             : 
     269        1216 :     return retval;
     270             : }
     271             : 
     272             : Datum
     273          42 : plpython3_inline_handler(PG_FUNCTION_ARGS)
     274             : {
     275          42 :     LOCAL_FCINFO(fake_fcinfo, 0);
     276          42 :     InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
     277             :     FmgrInfo    flinfo;
     278             :     PLyProcedure proc;
     279             :     PLyExecutionContext *exec_ctx;
     280             :     ErrorContextCallback plerrcontext;
     281             : 
     282          42 :     PLy_initialize();
     283             : 
     284             :     /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
     285          42 :     SPI_connect_ext(codeblock->atomic ? 0 : SPI_OPT_NONATOMIC);
     286             : 
     287         210 :     MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
     288         294 :     MemSet(&flinfo, 0, sizeof(flinfo));
     289          42 :     fake_fcinfo->flinfo = &flinfo;
     290          42 :     flinfo.fn_oid = InvalidOid;
     291          42 :     flinfo.fn_mcxt = CurrentMemoryContext;
     292             : 
     293        1764 :     MemSet(&proc, 0, sizeof(PLyProcedure));
     294          42 :     proc.mcxt = AllocSetContextCreate(TopMemoryContext,
     295             :                                       "__plpython_inline_block",
     296             :                                       ALLOCSET_DEFAULT_SIZES);
     297          42 :     proc.pyname = MemoryContextStrdup(proc.mcxt, "__plpython_inline_block");
     298          42 :     proc.langid = codeblock->langOid;
     299             : 
     300             :     /*
     301             :      * This is currently sufficient to get PLy_exec_function to work, but
     302             :      * someday we might need to be honest and use PLy_output_setup_func.
     303             :      */
     304          42 :     proc.result.typoid = VOIDOID;
     305             : 
     306             :     /*
     307             :      * Push execution context onto stack.  It is important that this get
     308             :      * popped again, so avoid putting anything that could throw error between
     309             :      * here and the PG_TRY.
     310             :      */
     311          42 :     exec_ctx = PLy_push_execution_context(codeblock->atomic);
     312             : 
     313          42 :     PG_TRY();
     314             :     {
     315             :         /*
     316             :          * Setup error traceback support for ereport().
     317             :          * plpython_inline_error_callback doesn't currently need exec_ctx, but
     318             :          * for consistency with plpython3_call_handler we do it the same way.
     319             :          */
     320          42 :         plerrcontext.callback = plpython_inline_error_callback;
     321          42 :         plerrcontext.arg = exec_ctx;
     322          42 :         plerrcontext.previous = error_context_stack;
     323          42 :         error_context_stack = &plerrcontext;
     324             : 
     325          42 :         PLy_procedure_compile(&proc, codeblock->source_text);
     326          42 :         exec_ctx->curr_proc = &proc;
     327          42 :         PLy_exec_function(fake_fcinfo, &proc);
     328             :     }
     329          22 :     PG_CATCH();
     330             :     {
     331          22 :         PLy_pop_execution_context();
     332          22 :         PLy_procedure_delete(&proc);
     333          22 :         PyErr_Clear();
     334          22 :         PG_RE_THROW();
     335             :     }
     336          20 :     PG_END_TRY();
     337             : 
     338             :     /* Destroy the execution context */
     339          20 :     PLy_pop_execution_context();
     340             : 
     341             :     /* Now clean up the transient procedure we made */
     342          20 :     PLy_procedure_delete(&proc);
     343             : 
     344          20 :     PG_RETURN_VOID();
     345             : }
     346             : 
     347             : static PLyTrigType
     348         500 : PLy_procedure_is_trigger(Form_pg_proc procStruct)
     349             : {
     350             :     PLyTrigType ret;
     351             : 
     352         500 :     switch (procStruct->prorettype)
     353             :     {
     354          48 :         case TRIGGEROID:
     355          48 :             ret = PLPY_TRIGGER;
     356          48 :             break;
     357           2 :         case EVENT_TRIGGEROID:
     358           2 :             ret = PLPY_EVENT_TRIGGER;
     359           2 :             break;
     360         450 :         default:
     361         450 :             ret = PLPY_NOT_TRIGGER;
     362         450 :             break;
     363             :     }
     364             : 
     365         500 :     return ret;
     366             : }
     367             : 
     368             : static void
     369         932 : plpython_error_callback(void *arg)
     370             : {
     371         932 :     PLyExecutionContext *exec_ctx = (PLyExecutionContext *) arg;
     372             : 
     373         932 :     if (exec_ctx->curr_proc)
     374             :     {
     375         926 :         if (exec_ctx->curr_proc->is_procedure)
     376           8 :             errcontext("PL/Python procedure \"%s\"",
     377             :                        PLy_procedure_name(exec_ctx->curr_proc));
     378             :         else
     379         918 :             errcontext("PL/Python function \"%s\"",
     380             :                        PLy_procedure_name(exec_ctx->curr_proc));
     381             :     }
     382         932 : }
     383             : 
     384             : static void
     385          56 : plpython_inline_error_callback(void *arg)
     386             : {
     387          56 :     errcontext("PL/Python anonymous code block");
     388          56 : }
     389             : 
     390             : PLyExecutionContext *
     391        2856 : PLy_current_execution_context(void)
     392             : {
     393        2856 :     if (PLy_execution_contexts == NULL)
     394           0 :         elog(ERROR, "no Python function is currently executing");
     395             : 
     396        2856 :     return PLy_execution_contexts;
     397             : }
     398             : 
     399             : MemoryContext
     400        1586 : PLy_get_scratch_context(PLyExecutionContext *context)
     401             : {
     402             :     /*
     403             :      * A scratch context might never be needed in a given plpython procedure,
     404             :      * so allocate it on first request.
     405             :      */
     406        1586 :     if (context->scratch_ctx == NULL)
     407         884 :         context->scratch_ctx =
     408         884 :             AllocSetContextCreate(TopTransactionContext,
     409             :                                   "PL/Python scratch context",
     410             :                                   ALLOCSET_DEFAULT_SIZES);
     411        1586 :     return context->scratch_ctx;
     412             : }
     413             : 
     414             : static PLyExecutionContext *
     415        1444 : PLy_push_execution_context(bool atomic_context)
     416             : {
     417             :     PLyExecutionContext *context;
     418             : 
     419             :     /* Pick a memory context similar to what SPI uses. */
     420             :     context = (PLyExecutionContext *)
     421        1444 :         MemoryContextAlloc(atomic_context ? TopTransactionContext : PortalContext,
     422             :                            sizeof(PLyExecutionContext));
     423        1444 :     context->curr_proc = NULL;
     424        1444 :     context->scratch_ctx = NULL;
     425        1444 :     context->next = PLy_execution_contexts;
     426        1444 :     PLy_execution_contexts = context;
     427        1444 :     return context;
     428             : }
     429             : 
     430             : static void
     431        1444 : PLy_pop_execution_context(void)
     432             : {
     433        1444 :     PLyExecutionContext *context = PLy_execution_contexts;
     434             : 
     435        1444 :     if (context == NULL)
     436           0 :         elog(ERROR, "no Python function is currently executing");
     437             : 
     438        1444 :     PLy_execution_contexts = context->next;
     439             : 
     440        1444 :     if (context->scratch_ctx)
     441         850 :         MemoryContextDelete(context->scratch_ctx);
     442        1444 :     pfree(context);
     443        1444 : }

Generated by: LCOV version 1.16