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

Generated by: LCOV version 1.13