Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions | Variables

plpy_main.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "plpython.h"
#include "plpy_main.h"
#include "plpy_elog.h"
#include "plpy_exec.h"
#include "plpy_plpymodule.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
Include dependency graph for plpy_main.c:

Go to the source code of this file.

Functions

void _PG_init (void)
Datum plpython_validator (PG_FUNCTION_ARGS)
Datum plpython_call_handler (PG_FUNCTION_ARGS)
Datum plpython_inline_handler (PG_FUNCTION_ARGS)
Datum plpython2_validator (PG_FUNCTION_ARGS)
Datum plpython2_call_handler (PG_FUNCTION_ARGS)
Datum plpython2_inline_handler (PG_FUNCTION_ARGS)
 PG_FUNCTION_INFO_V1 (plpython_validator)
 PG_FUNCTION_INFO_V1 (plpython_call_handler)
 PG_FUNCTION_INFO_V1 (plpython_inline_handler)
 PG_FUNCTION_INFO_V1 (plpython2_validator)
 PG_FUNCTION_INFO_V1 (plpython2_call_handler)
 PG_FUNCTION_INFO_V1 (plpython2_inline_handler)
static bool PLy_procedure_is_trigger (Form_pg_proc procStruct)
static void plpython_error_callback (void *arg)
static void plpython_inline_error_callback (void *arg)
static void PLy_init_interp (void)
static PLyExecutionContextPLy_push_execution_context (void)
static void PLy_pop_execution_context (void)
PLyExecutionContextPLy_current_execution_context (void)

Variables

 PG_MODULE_MAGIC
static const int plpython_python_version = PY_MAJOR_VERSION
PyObject * PLy_interp_globals = NULL
static PLyExecutionContextPLy_execution_contexts = NULL

Function Documentation

void _PG_init ( void   ) 

Definition at line 85 of file plpy_main.c.

References ereport, errdetail(), errhint(), errmsg(), explicit_subtransactions, FATAL, find_rendezvous_variable(), init_procedure_caches(), pg_bindtextdomain(), plpython_python_version, PLy_elog(), PLy_init_interp(), PLy_init_plpy(), and TEXTDOMAIN.

{
    /* Be sure we do initialization only once (should be redundant now) */
    static bool inited = false;
    const int **version_ptr;

    if (inited)
        return;

    /* Be sure we don't run Python 2 and 3 in the same session (might crash) */
    version_ptr = (const int **) find_rendezvous_variable("plpython_python_version");
    if (!(*version_ptr))
        *version_ptr = &plpython_python_version;
    else
    {
        if (**version_ptr != plpython_python_version)
            ereport(FATAL,
                    (errmsg("Python major version mismatch in session"),
                     errdetail("This session has previously used Python major version %d, and it is now attempting to use Python major version %d.",
                               **version_ptr, plpython_python_version),
                     errhint("Start a new session to use a different Python major version.")));
    }

    pg_bindtextdomain(TEXTDOMAIN);

#if PY_MAJOR_VERSION >= 3
    PyImport_AppendInittab("plpy", PyInit_plpy);
#endif
    Py_Initialize();
#if PY_MAJOR_VERSION >= 3
    PyImport_ImportModule("plpy");
#endif
    PLy_init_interp();
    PLy_init_plpy();
    if (PyErr_Occurred())
        PLy_elog(FATAL, "untrapped error in initialization");

    init_procedure_caches();

    explicit_subtransactions = NIL;

    PLy_execution_contexts = NULL;

    inited = true;
}

PG_FUNCTION_INFO_V1 ( plpython_validator   ) 
PG_FUNCTION_INFO_V1 ( plpython_call_handler   ) 
PG_FUNCTION_INFO_V1 ( plpython_inline_handler   ) 
PG_FUNCTION_INFO_V1 ( plpython2_validator   ) 
PG_FUNCTION_INFO_V1 ( plpython2_call_handler   ) 
PG_FUNCTION_INFO_V1 ( plpython2_inline_handler   ) 
Datum plpython2_call_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 258 of file plpy_main.c.

References plpython_call_handler().

{
    return plpython_call_handler(fcinfo);
}

Datum plpython2_inline_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 332 of file plpy_main.c.

References plpython_inline_handler().

{
    return plpython_inline_handler(fcinfo);
}

Datum plpython2_validator ( PG_FUNCTION_ARGS   ) 

Definition at line 186 of file plpy_main.c.

References plpython_validator().

{
    return plpython_validator(fcinfo);
}

Datum plpython_call_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 193 of file plpy_main.c.

References ErrorContextCallback::callback, CALLED_AS_TRIGGER, PLyExecutionContext::curr_proc, elog, ERROR, error_context_stack, InvalidOid, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_exec_function(), PLy_exec_trigger(), PLy_pop_execution_context(), PLy_procedure_get(), PLy_push_execution_context(), PointerGetDatum, ErrorContextCallback::previous, RelationGetRelid, SPI_connect(), and SPI_OK_CONNECT.

Referenced by plpython2_call_handler().

{
    Datum       retval;
    PLyExecutionContext *exec_ctx;
    ErrorContextCallback plerrcontext;

    /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
    if (SPI_connect() != SPI_OK_CONNECT)
        elog(ERROR, "SPI_connect failed");

    /*
     * Push execution context onto stack.  It is important that this get
     * popped again, so avoid putting anything that could throw error between
     * here and the PG_TRY.  (plpython_error_callback expects the stack entry
     * to be there, so we have to make the context first.)
     */
    exec_ctx = PLy_push_execution_context();

    /*
     * Setup error traceback support for ereport()
     */
    plerrcontext.callback = plpython_error_callback;
    plerrcontext.previous = error_context_stack;
    error_context_stack = &plerrcontext;

    PG_TRY();
    {
        Oid         funcoid = fcinfo->flinfo->fn_oid;
        PLyProcedure *proc;

        if (CALLED_AS_TRIGGER(fcinfo))
        {
            Relation    tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
            HeapTuple   trv;

            proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), true);
            exec_ctx->curr_proc = proc;
            trv = PLy_exec_trigger(fcinfo, proc);
            retval = PointerGetDatum(trv);
        }
        else
        {
            proc = PLy_procedure_get(funcoid, InvalidOid, false);
            exec_ctx->curr_proc = proc;
            retval = PLy_exec_function(fcinfo, proc);
        }
    }
    PG_CATCH();
    {
        PLy_pop_execution_context();
        PyErr_Clear();
        PG_RE_THROW();
    }
    PG_END_TRY();

    /* Pop the error context stack */
    error_context_stack = plerrcontext.previous;
    /* ... and then the execution context */
    PLy_pop_execution_context();

    return retval;
}

static void plpython_error_callback ( void *  arg  )  [static]

Definition at line 347 of file plpy_main.c.

References PLyExecutionContext::curr_proc, errcontext, PLy_current_execution_context(), and PLy_procedure_name().

{
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();

    if (exec_ctx->curr_proc)
        errcontext("PL/Python function \"%s\"",
                   PLy_procedure_name(exec_ctx->curr_proc));
}

static void plpython_inline_error_callback ( void *  arg  )  [static]

Definition at line 357 of file plpy_main.c.

References errcontext.

{
    errcontext("PL/Python anonymous code block");
}

Datum plpython_inline_handler ( PG_FUNCTION_ARGS   ) 

Definition at line 265 of file plpy_main.c.

References ErrorContextCallback::callback, PLyExecutionContext::curr_proc, CurrentMemoryContext, PLyTypeOutput::d, DatumGetPointer, elog, ERROR, error_context_stack, FunctionCallInfoData::flinfo, FmgrInfo::fn_mcxt, FmgrInfo::fn_oid, MemSet, PLyTypeInfo::out, PG_CATCH, PG_END_TRY, PG_GETARG_DATUM, PG_RE_THROW, PG_RETURN_VOID, PG_TRY, PLy_exec_function(), PLy_pop_execution_context(), PLy_procedure_compile(), PLy_procedure_delete(), PLy_push_execution_context(), PLy_strdup(), ErrorContextCallback::previous, PLyProcedure::pyname, PLyProcedure::result, InlineCodeBlock::source_text, SPI_connect(), and PLyObToDatum::typoid.

Referenced by plpython2_inline_handler().

{
    InlineCodeBlock *codeblock = (InlineCodeBlock *) DatumGetPointer(PG_GETARG_DATUM(0));
    FunctionCallInfoData fake_fcinfo;
    FmgrInfo    flinfo;
    PLyProcedure proc;
    PLyExecutionContext *exec_ctx;
    ErrorContextCallback plerrcontext;

    /* Note: SPI_finish() happens in plpy_exec.c, which is dubious design */
    if (SPI_connect() != SPI_OK_CONNECT)
        elog(ERROR, "SPI_connect failed");

    MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
    MemSet(&flinfo, 0, sizeof(flinfo));
    fake_fcinfo.flinfo = &flinfo;
    flinfo.fn_oid = InvalidOid;
    flinfo.fn_mcxt = CurrentMemoryContext;

    MemSet(&proc, 0, sizeof(PLyProcedure));
    proc.pyname = PLy_strdup("__plpython_inline_block");
    proc.result.out.d.typoid = VOIDOID;

    /*
     * Push execution context onto stack.  It is important that this get
     * popped again, so avoid putting anything that could throw error between
     * here and the PG_TRY.  (plpython_inline_error_callback doesn't currently
     * need the stack entry, but for consistency with plpython_call_handler we
     * do it in this order.)
     */
    exec_ctx = PLy_push_execution_context();

    /*
     * Setup error traceback support for ereport()
     */
    plerrcontext.callback = plpython_inline_error_callback;
    plerrcontext.previous = error_context_stack;
    error_context_stack = &plerrcontext;

    PG_TRY();
    {
        PLy_procedure_compile(&proc, codeblock->source_text);
        exec_ctx->curr_proc = &proc;
        PLy_exec_function(&fake_fcinfo, &proc);
    }
    PG_CATCH();
    {
        PLy_pop_execution_context();
        PLy_procedure_delete(&proc);
        PyErr_Clear();
        PG_RE_THROW();
    }
    PG_END_TRY();

    /* Pop the error context stack */
    error_context_stack = plerrcontext.previous;
    /* ... and then the execution context */
    PLy_pop_execution_context();

    /* Now clean up the transient procedure we made */
    PLy_procedure_delete(&proc);

    PG_RETURN_VOID();
}

Datum plpython_validator ( PG_FUNCTION_ARGS   ) 

Definition at line 156 of file plpy_main.c.

References check_function_bodies, elog, ERROR, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, PG_GETARG_OID, PG_RETURN_VOID, PLy_procedure_get(), PLy_procedure_is_trigger(), PROCOID, ReleaseSysCache(), and SearchSysCache1.

Referenced by plpython2_validator().

{
    Oid         funcoid = PG_GETARG_OID(0);
    HeapTuple   tuple;
    Form_pg_proc procStruct;
    bool        is_trigger;

    if (!check_function_bodies)
    {
        PG_RETURN_VOID();
    }

    /* Get the new function's pg_proc entry */
    tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "cache lookup failed for function %u", funcoid);
    procStruct = (Form_pg_proc) GETSTRUCT(tuple);

    is_trigger = PLy_procedure_is_trigger(procStruct);

    ReleaseSysCache(tuple);

    /* We can't validate triggers against any particular table ... */
    PLy_procedure_get(funcoid, InvalidOid, is_trigger);

    PG_RETURN_VOID();
}

PLyExecutionContext* PLy_current_execution_context ( void   ) 
void PLy_init_interp ( void   )  [static]

Definition at line 136 of file plpy_main.c.

References ERROR, NULL, PLy_elog(), and PLy_interp_globals.

Referenced by _PG_init().

{
    static PyObject *PLy_interp_safe_globals = NULL;
    PyObject   *mainmod;

    mainmod = PyImport_AddModule("__main__");
    if (mainmod == NULL || PyErr_Occurred())
        PLy_elog(ERROR, "could not import \"__main__\" module");
    Py_INCREF(mainmod);
    PLy_interp_globals = PyModule_GetDict(mainmod);
    PLy_interp_safe_globals = PyDict_New();
    if (PLy_interp_safe_globals == NULL)
        PLy_elog(ERROR, "could not create globals");
    PyDict_SetItemString(PLy_interp_globals, "GD", PLy_interp_safe_globals);
    Py_DECREF(mainmod);
    if (PLy_interp_globals == NULL || PyErr_Occurred())
        PLy_elog(ERROR, "could not initialize globals");
}

static void PLy_pop_execution_context ( void   )  [static]

Definition at line 388 of file plpy_main.c.

References elog, ERROR, MemoryContextDelete(), PLyExecutionContext::next, NULL, PLy_free(), and PLyExecutionContext::scratch_ctx.

Referenced by plpython_call_handler(), and plpython_inline_handler().

{
    PLyExecutionContext *context = PLy_execution_contexts;

    if (context == NULL)
        elog(ERROR, "no Python function is currently executing");

    PLy_execution_contexts = context->next;

    MemoryContextDelete(context->scratch_ctx);
    PLy_free(context);
}

static bool PLy_procedure_is_trigger ( Form_pg_proc  procStruct  )  [static]

Definition at line 339 of file plpy_main.c.

References OPAQUEOID, and TRIGGEROID.

Referenced by plpython_validator().

{
    return (procStruct->prorettype == TRIGGEROID ||
            (procStruct->prorettype == OPAQUEOID &&
             procStruct->pronargs == 0));
}

static PLyExecutionContext * PLy_push_execution_context ( void   )  [static]

Variable Documentation

Definition at line 54 of file plpy_main.c.

const int plpython_python_version = PY_MAJOR_VERSION [static]

Definition at line 75 of file plpy_main.c.

Referenced by _PG_init().

Definition at line 81 of file plpy_main.c.

PyObject* PLy_interp_globals = NULL

Definition at line 78 of file plpy_main.c.

Referenced by PLy_init_interp(), and PLy_procedure_compile().