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

Generated by: LCOV version 1.14