Header And Logo

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

Functions

plpy_exec.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/typcache.h"
#include "plpython.h"
#include "plpy_exec.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
Include dependency graph for plpy_exec.c:

Go to the source code of this file.

Functions

static PyObject * PLy_function_build_args (FunctionCallInfo fcinfo, PLyProcedure *proc)
static void PLy_function_delete_args (PLyProcedure *proc)
static void plpython_return_error_callback (void *arg)
static PyObject * PLy_trigger_build_args (FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *rv)
static HeapTuple PLy_modify_tuple (PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, HeapTuple otup)
static void plpython_trigger_error_callback (void *arg)
static PyObject * PLy_procedure_call (PLyProcedure *proc, char *kargs, PyObject *vargs)
static void PLy_abort_open_subtransactions (int save_subxact_level)
Datum PLy_exec_function (FunctionCallInfo fcinfo, PLyProcedure *proc)
HeapTuple PLy_exec_trigger (FunctionCallInfo fcinfo, PLyProcedure *proc)

Function Documentation

static void plpython_return_error_callback ( void *  arg  )  [static]

Definition at line 446 of file plpy_exec.c.

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

{
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();

    if (exec_ctx->curr_proc)
        errcontext("while creating return value");
}

static void plpython_trigger_error_callback ( void *  arg  )  [static]

Definition at line 774 of file plpy_exec.c.

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

{
    PLyExecutionContext *exec_ctx = PLy_current_execution_context();

    if (exec_ctx->curr_proc)
        errcontext("while modifying trigger row");
}

static void PLy_abort_open_subtransactions ( int  save_subxact_level  )  [static]
Datum PLy_exec_function ( FunctionCallInfo  fcinfo,
PLyProcedure proc 
)

Definition at line 45 of file plpy_exec.c.

References ReturnSetInfo::allowedModes, Assert, ErrorContextCallback::callback, PLyTypeOutput::d, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, error_context_stack, ExprEndResult, PLyObToDatum::func, InputFunctionCall(), PLyTypeInfo::is_rowtype, PLyProcedure::is_setof, IsA, ReturnSetInfo::isDone, FunctionCallInfoData::isnull, lookup_rowtype_tupdesc(), NULL, PLyTypeInfo::out, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLy_function_build_args(), PLy_function_delete_args(), PLy_procedure_call(), PLyObject_ToCompositeDatum(), ErrorContextCallback::previous, RECORDOID, PLyProcedure::result, FunctionCallInfoData::resultinfo, ReturnSetInfo::returnMode, PLyProcedure::setof, SPI_finish(), SPI_OK_FINISH, PLyObToDatum::typfunc, PLyObToDatum::typioparam, PLyObToDatum::typmod, PLyObToDatum::typoid, and VOIDOID.

Referenced by plpython_call_handler(), and plpython_inline_handler().

{
    Datum       rv;
    PyObject   *volatile plargs = NULL;
    PyObject   *volatile plrv = NULL;
    ErrorContextCallback plerrcontext;

    PG_TRY();
    {
        if (!proc->is_setof || proc->setof == NULL)
        {
            /*
             * Simple type returning function or first time for SETOF
             * function: actually execute the function.
             */
            plargs = PLy_function_build_args(fcinfo, proc);
            plrv = PLy_procedure_call(proc, "args", plargs);
            if (!proc->is_setof)
            {
                /*
                 * SETOF function parameters will be deleted when last row is
                 * returned
                 */
                PLy_function_delete_args(proc);
            }
            Assert(plrv != NULL);
        }

        /*
         * If it returns a set, call the iterator to get the next return item.
         * We stay in the SPI context while doing this, because PyIter_Next()
         * calls back into Python code which might contain SPI calls.
         */
        if (proc->is_setof)
        {
            bool        has_error = false;
            ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

            if (proc->setof == NULL)
            {
                /* first time -- do checks and setup */
                if (!rsi || !IsA(rsi, ReturnSetInfo) ||
                    (rsi->allowedModes & SFRM_ValuePerCall) == 0)
                {
                    ereport(ERROR,
                            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                             errmsg("unsupported set function return mode"),
                             errdetail("PL/Python set-returning functions only support returning only value per call.")));
                }
                rsi->returnMode = SFRM_ValuePerCall;

                /* Make iterator out of returned object */
                proc->setof = PyObject_GetIter(plrv);
                Py_DECREF(plrv);
                plrv = NULL;

                if (proc->setof == NULL)
                    ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                             errmsg("returned object cannot be iterated"),
                             errdetail("PL/Python set-returning functions must return an iterable object.")));
            }

            /* Fetch next from iterator */
            plrv = PyIter_Next(proc->setof);
            if (plrv)
                rsi->isDone = ExprMultipleResult;
            else
            {
                rsi->isDone = ExprEndResult;
                has_error = PyErr_Occurred() != NULL;
            }

            if (rsi->isDone == ExprEndResult)
            {
                /* Iterator is exhausted or error happened */
                Py_DECREF(proc->setof);
                proc->setof = NULL;

                Py_XDECREF(plargs);
                Py_XDECREF(plrv);

                PLy_function_delete_args(proc);

                if (has_error)
                    PLy_elog(ERROR, "error fetching next item from iterator");

                /* Disconnect from the SPI manager before returning */
                if (SPI_finish() != SPI_OK_FINISH)
                    elog(ERROR, "SPI_finish failed");

                fcinfo->isnull = true;
                return (Datum) NULL;
            }
        }

        /*
         * Disconnect from SPI manager and then create the return values datum
         * (if the input function does a palloc for it this must not be
         * allocated in the SPI memory context because SPI_finish would free
         * it).
         */
        if (SPI_finish() != SPI_OK_FINISH)
            elog(ERROR, "SPI_finish failed");

        plerrcontext.callback = plpython_return_error_callback;
        plerrcontext.previous = error_context_stack;
        error_context_stack = &plerrcontext;

        /*
         * If the function is declared to return void, the Python return value
         * must be None. For void-returning functions, we also treat a None
         * return value as a special "void datum" rather than NULL (as is the
         * case for non-void-returning functions).
         */
        if (proc->result.out.d.typoid == VOIDOID)
        {
            if (plrv != Py_None)
                ereport(ERROR,
                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                         errmsg("PL/Python function with return type \"void\" did not return None")));

            fcinfo->isnull = false;
            rv = (Datum) 0;
        }
        else if (plrv == Py_None)
        {
            fcinfo->isnull = true;
            if (proc->result.is_rowtype < 1)
                rv = InputFunctionCall(&proc->result.out.d.typfunc,
                                       NULL,
                                       proc->result.out.d.typioparam,
                                       -1);
            else
                /* Tuple as None */
                rv = (Datum) NULL;
        }
        else if (proc->result.is_rowtype >= 1)
        {
            TupleDesc   desc;

            /* make sure it's not an unnamed record */
            Assert((proc->result.out.d.typoid == RECORDOID &&
                    proc->result.out.d.typmod != -1) ||
                   (proc->result.out.d.typoid != RECORDOID &&
                    proc->result.out.d.typmod == -1));

            desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
                                          proc->result.out.d.typmod);

            rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv);
            fcinfo->isnull = (rv == (Datum) NULL);
        }
        else
        {
            fcinfo->isnull = false;
            rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv);
        }
    }
    PG_CATCH();
    {
        Py_XDECREF(plargs);
        Py_XDECREF(plrv);

        /*
         * If there was an error the iterator might have not been exhausted
         * yet. Set it to NULL so the next invocation of the function will
         * start the iteration again.
         */
        Py_XDECREF(proc->setof);
        proc->setof = NULL;

        PG_RE_THROW();
    }
    PG_END_TRY();

    error_context_stack = plerrcontext.previous;

    Py_XDECREF(plargs);
    Py_DECREF(plrv);

    return rv;
}

HeapTuple PLy_exec_trigger ( FunctionCallInfo  fcinfo,
PLyProcedure proc 
)

Definition at line 240 of file plpy_exec.c.

References Assert, CALLED_AS_TRIGGER, FunctionCallInfoData::context, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, pg_strcasecmp(), PG_TRY, PLy_input_tuple_funcs(), PLy_modify_tuple(), PLy_output_tuple_funcs(), PLy_procedure_call(), PLy_trigger_build_args(), PLyUnicode_AsString(), RelationData::rd_att, PLyProcedure::result, SPI_finish(), SPI_OK_FINISH, TriggerData::tg_event, TriggerData::tg_relation, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, and WARNING.

Referenced by plpython_call_handler().

{
    HeapTuple   rv = NULL;
    PyObject   *volatile plargs = NULL;
    PyObject   *volatile plrv = NULL;
    TriggerData *tdata;

    Assert(CALLED_AS_TRIGGER(fcinfo));

    /*
     * Input/output conversion for trigger tuples.  Use the result TypeInfo
     * variable to store the tuple conversion info.  We do this over again on
     * each call to cover the possibility that the relation's tupdesc changed
     * since the trigger was last called. PLy_input_tuple_funcs and
     * PLy_output_tuple_funcs are responsible for not doing repetitive work.
     */
    tdata = (TriggerData *) fcinfo->context;

    PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
    PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);

    PG_TRY();
    {
        plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
        plrv = PLy_procedure_call(proc, "TD", plargs);

        Assert(plrv != NULL);

        /*
         * Disconnect from SPI manager
         */
        if (SPI_finish() != SPI_OK_FINISH)
            elog(ERROR, "SPI_finish failed");

        /*
         * return of None means we're happy with the tuple
         */
        if (plrv != Py_None)
        {
            char       *srv;

            if (PyString_Check(plrv))
                srv = PyString_AsString(plrv);
            else if (PyUnicode_Check(plrv))
                srv = PLyUnicode_AsString(plrv);
            else
            {
                ereport(ERROR,
                        (errcode(ERRCODE_DATA_EXCEPTION),
                    errmsg("unexpected return value from trigger procedure"),
                         errdetail("Expected None or a string.")));
                srv = NULL;     /* keep compiler quiet */
            }

            if (pg_strcasecmp(srv, "SKIP") == 0)
                rv = NULL;
            else if (pg_strcasecmp(srv, "MODIFY") == 0)
            {
                TriggerData *tdata = (TriggerData *) fcinfo->context;

                if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event) ||
                    TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
                    rv = PLy_modify_tuple(proc, plargs, tdata, rv);
                else
                    ereport(WARNING,
                            (errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored")));
            }
            else if (pg_strcasecmp(srv, "OK") != 0)
            {
                /*
                 * accept "OK" as an alternative to None; otherwise, raise an
                 * error
                 */
                ereport(ERROR,
                        (errcode(ERRCODE_DATA_EXCEPTION),
                    errmsg("unexpected return value from trigger procedure"),
                         errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\".")));
            }
        }
    }
    PG_CATCH();
    {
        Py_XDECREF(plargs);
        Py_XDECREF(plrv);

        PG_RE_THROW();
    }
    PG_END_TRY();

    Py_DECREF(plargs);
    Py_DECREF(plrv);

    return rv;
}

static PyObject * PLy_function_build_args ( FunctionCallInfo  fcinfo,
PLyProcedure proc 
) [static]

Definition at line 338 of file plpy_exec.c.

References FunctionCallInfoData::arg, arg, PLyProcedure::argnames, FunctionCallInfoData::argnull, PLyProcedure::args, PLyTypeOutput::d, PLyTypeInput::d, DatumGetHeapTupleHeader, ereport, errcode(), errmsg(), ERROR, PLyDatumToOb::func, get_call_result_type(), PLyProcedure::globals, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, PLyTypeInfo::in, PLyTypeInfo::is_rowtype, lookup_rowtype_tupdesc(), PLyProcedure::nargs, NULL, PLyTypeInfo::out, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLy_input_tuple_funcs(), PLy_output_record_funcs(), PLyDict_FromTuple(), RECORDOID, ReleaseTupleDesc, PLyProcedure::result, HeapTupleData::t_data, HeapTupleData::t_len, TYPEFUNC_COMPOSITE, and PLyObToDatum::typoid.

Referenced by PLy_exec_function().

{
    PyObject   *volatile arg = NULL;
    PyObject   *volatile args = NULL;
    int         i;

    PG_TRY();
    {
        args = PyList_New(proc->nargs);
        for (i = 0; i < proc->nargs; i++)
        {
            if (proc->args[i].is_rowtype > 0)
            {
                if (fcinfo->argnull[i])
                    arg = NULL;
                else
                {
                    HeapTupleHeader td;
                    Oid         tupType;
                    int32       tupTypmod;
                    TupleDesc   tupdesc;
                    HeapTupleData tmptup;

                    td = DatumGetHeapTupleHeader(fcinfo->arg[i]);
                    /* Extract rowtype info and find a tupdesc */
                    tupType = HeapTupleHeaderGetTypeId(td);
                    tupTypmod = HeapTupleHeaderGetTypMod(td);
                    tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);

                    /* Set up I/O funcs if not done yet */
                    if (proc->args[i].is_rowtype != 1)
                        PLy_input_tuple_funcs(&(proc->args[i]), tupdesc);

                    /* Build a temporary HeapTuple control structure */
                    tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
                    tmptup.t_data = td;

                    arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc);
                    ReleaseTupleDesc(tupdesc);
                }
            }
            else
            {
                if (fcinfo->argnull[i])
                    arg = NULL;
                else
                {
                    arg = (proc->args[i].in.d.func) (&(proc->args[i].in.d),
                                                     fcinfo->arg[i]);
                }
            }

            if (arg == NULL)
            {
                Py_INCREF(Py_None);
                arg = Py_None;
            }

            if (PyList_SetItem(args, i, arg) == -1)
                PLy_elog(ERROR, "PyList_SetItem() failed, while setting up arguments");

            if (proc->argnames && proc->argnames[i] &&
            PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1)
                PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments");
            arg = NULL;
        }

        /* Set up output conversion for functions returning RECORD */
        if (proc->result.out.d.typoid == RECORDOID)
        {
            TupleDesc   desc;

            if (get_call_result_type(fcinfo, NULL, &desc) != TYPEFUNC_COMPOSITE)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("function returning record called in context "
                                "that cannot accept type record")));

            /* cache the output conversion functions */
            PLy_output_record_funcs(&(proc->result), desc);
        }
    }
    PG_CATCH();
    {
        Py_XDECREF(arg);
        Py_XDECREF(args);

        PG_RE_THROW();
    }
    PG_END_TRY();

    return args;
}

static void PLy_function_delete_args ( PLyProcedure proc  )  [static]

Definition at line 433 of file plpy_exec.c.

References PLyProcedure::argnames, PLyProcedure::globals, i, and PLyProcedure::nargs.

Referenced by PLy_exec_function().

{
    int         i;

    if (!proc->argnames)
        return;

    for (i = 0; i < proc->nargs; i++)
        if (proc->argnames[i])
            PyDict_DelItemString(proc->globals, proc->argnames[i]);
}

static HeapTuple PLy_modify_tuple ( PLyProcedure proc,
PyObject *  pltd,
TriggerData tdata,
HeapTuple  otup 
) [static]

Definition at line 633 of file plpy_exec.c.

References tupleDesc::attrs, PLyObToTuple::atts, ErrorContextCallback::callback, elog, ereport, errmsg(), ERROR, error_context_stack, FATAL, PLyObToDatum::func, i, InputFunctionCall(), NULL, PLyTypeInfo::out, palloc(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLyUnicode_AsString(), ErrorContextCallback::previous, PLyTypeOutput::r, RelationData::rd_att, PLyProcedure::result, SPI_ERROR_NOATTRIBUTE, SPI_fnumber(), SPI_modifytuple(), SPI_result, TriggerData::tg_relation, PLyObToDatum::typfunc, and PLyObToDatum::typioparam.

Referenced by PLy_exec_trigger().

{
    PyObject   *volatile plntup;
    PyObject   *volatile plkeys;
    PyObject   *volatile platt;
    PyObject   *volatile plval;
    PyObject   *volatile plstr;
    HeapTuple   rtup;
    int         natts,
                i,
                attn,
                atti;
    int        *volatile modattrs;
    Datum      *volatile modvalues;
    char       *volatile modnulls;
    TupleDesc   tupdesc;
    ErrorContextCallback plerrcontext;

    plerrcontext.callback = plpython_trigger_error_callback;
    plerrcontext.previous = error_context_stack;
    error_context_stack = &plerrcontext;

    plntup = plkeys = platt = plval = plstr = NULL;
    modattrs = NULL;
    modvalues = NULL;
    modnulls = NULL;

    PG_TRY();
    {
        if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
            ereport(ERROR,
                    (errmsg("TD[\"new\"] deleted, cannot modify row")));
        if (!PyDict_Check(plntup))
            ereport(ERROR,
                    (errmsg("TD[\"new\"] is not a dictionary")));
        Py_INCREF(plntup);

        plkeys = PyDict_Keys(plntup);
        natts = PyList_Size(plkeys);

        modattrs = (int *) palloc(natts * sizeof(int));
        modvalues = (Datum *) palloc(natts * sizeof(Datum));
        modnulls = (char *) palloc(natts * sizeof(char));

        tupdesc = tdata->tg_relation->rd_att;

        for (i = 0; i < natts; i++)
        {
            char       *plattstr;

            platt = PyList_GetItem(plkeys, i);
            if (PyString_Check(platt))
                plattstr = PyString_AsString(platt);
            else if (PyUnicode_Check(platt))
                plattstr = PLyUnicode_AsString(platt);
            else
            {
                ereport(ERROR,
                        (errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", i)));
                plattstr = NULL;    /* keep compiler quiet */
            }
            attn = SPI_fnumber(tupdesc, plattstr);
            if (attn == SPI_ERROR_NOATTRIBUTE)
                ereport(ERROR,
                        (errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
                                plattstr)));
            atti = attn - 1;

            plval = PyDict_GetItem(plntup, platt);
            if (plval == NULL)
                elog(FATAL, "Python interpreter is probably corrupted");

            Py_INCREF(plval);

            modattrs[i] = attn;

            if (tupdesc->attrs[atti]->attisdropped)
            {
                modvalues[i] = (Datum) 0;
                modnulls[i] = 'n';
            }
            else if (plval != Py_None)
            {
                PLyObToDatum *att = &proc->result.out.r.atts[atti];

                modvalues[i] = (att->func) (att,
                                            tupdesc->attrs[atti]->atttypmod,
                                            plval);
                modnulls[i] = ' ';
            }
            else
            {
                modvalues[i] =
                    InputFunctionCall(&proc->result.out.r.atts[atti].typfunc,
                                      NULL,
                                    proc->result.out.r.atts[atti].typioparam,
                                      tupdesc->attrs[atti]->atttypmod);
                modnulls[i] = 'n';
            }

            Py_DECREF(plval);
            plval = NULL;
        }

        rtup = SPI_modifytuple(tdata->tg_relation, otup, natts,
                               modattrs, modvalues, modnulls);
        if (rtup == NULL)
            elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result);
    }
    PG_CATCH();
    {
        Py_XDECREF(plntup);
        Py_XDECREF(plkeys);
        Py_XDECREF(plval);
        Py_XDECREF(plstr);

        if (modnulls)
            pfree(modnulls);
        if (modvalues)
            pfree(modvalues);
        if (modattrs)
            pfree(modattrs);

        PG_RE_THROW();
    }
    PG_END_TRY();

    Py_DECREF(plntup);
    Py_DECREF(plkeys);

    pfree(modattrs);
    pfree(modvalues);
    pfree(modnulls);

    error_context_stack = plerrcontext.previous;

    return rtup;
}

static PyObject * PLy_procedure_call ( PLyProcedure proc,
char *  kargs,
PyObject *  vargs 
) [static]

Definition at line 784 of file plpy_exec.c.

References Assert, PLyProcedure::code, ERROR, explicit_subtransactions, PLyProcedure::globals, list_length(), NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_abort_open_subtransactions(), and PLy_elog().

Referenced by PLy_exec_function(), and PLy_exec_trigger().

{
    PyObject   *rv;
    int volatile save_subxact_level = list_length(explicit_subtransactions);

    PyDict_SetItemString(proc->globals, kargs, vargs);

    PG_TRY();
    {
#if PY_VERSION_HEX >= 0x03020000
        rv = PyEval_EvalCode(proc->code,
                             proc->globals, proc->globals);
#else
        rv = PyEval_EvalCode((PyCodeObject *) proc->code,
                             proc->globals, proc->globals);
#endif

        /*
         * Since plpy will only let you close subtransactions that you
         * started, you cannot *unnest* subtransactions, only *nest* them
         * without closing.
         */
        Assert(list_length(explicit_subtransactions) >= save_subxact_level);
    }
    PG_CATCH();
    {
        PLy_abort_open_subtransactions(save_subxact_level);
        PG_RE_THROW();
    }
    PG_END_TRY();

    PLy_abort_open_subtransactions(save_subxact_level);

    /* If the Python code returned an error, propagate it */
    if (rv == NULL)
        PLy_elog(ERROR, NULL);

    return rv;
}

static PyObject * PLy_trigger_build_args ( FunctionCallInfo  fcinfo,
PLyProcedure proc,
HeapTuple rv 
) [static]

Definition at line 455 of file plpy_exec.c.

References FunctionCallInfoData::context, DatumGetCString, DirectFunctionCall1, elog, ERROR, i, NULL, ObjectIdGetDatum, oidout(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLyDict_FromTuple(), RelationData::rd_att, RelationData::rd_id, PLyProcedure::result, SPI_getnspname(), SPI_getrelname(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgname, Trigger::tgnargs, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_TRUNCATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TRIGGER_FIRED_FOR_STATEMENT, and TRIGGER_FIRED_INSTEAD.

Referenced by PLy_exec_trigger().

{
    TriggerData *tdata = (TriggerData *) fcinfo->context;
    PyObject   *pltname,
               *pltevent,
               *pltwhen,
               *pltlevel,
               *pltrelid,
               *plttablename,
               *plttableschema;
    PyObject   *pltargs,
               *pytnew,
               *pytold;
    PyObject   *volatile pltdata = NULL;
    char       *stroid;

    PG_TRY();
    {
        pltdata = PyDict_New();
        if (!pltdata)
            PLy_elog(ERROR, "could not create new dictionary while building trigger arguments");

        pltname = PyString_FromString(tdata->tg_trigger->tgname);
        PyDict_SetItemString(pltdata, "name", pltname);
        Py_DECREF(pltname);

        stroid = DatumGetCString(DirectFunctionCall1(oidout,
                               ObjectIdGetDatum(tdata->tg_relation->rd_id)));
        pltrelid = PyString_FromString(stroid);
        PyDict_SetItemString(pltdata, "relid", pltrelid);
        Py_DECREF(pltrelid);
        pfree(stroid);

        stroid = SPI_getrelname(tdata->tg_relation);
        plttablename = PyString_FromString(stroid);
        PyDict_SetItemString(pltdata, "table_name", plttablename);
        Py_DECREF(plttablename);
        pfree(stroid);

        stroid = SPI_getnspname(tdata->tg_relation);
        plttableschema = PyString_FromString(stroid);
        PyDict_SetItemString(pltdata, "table_schema", plttableschema);
        Py_DECREF(plttableschema);
        pfree(stroid);

        if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
            pltwhen = PyString_FromString("BEFORE");
        else if (TRIGGER_FIRED_AFTER(tdata->tg_event))
            pltwhen = PyString_FromString("AFTER");
        else if (TRIGGER_FIRED_INSTEAD(tdata->tg_event))
            pltwhen = PyString_FromString("INSTEAD OF");
        else
        {
            elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event);
            pltwhen = NULL;     /* keep compiler quiet */
        }
        PyDict_SetItemString(pltdata, "when", pltwhen);
        Py_DECREF(pltwhen);

        if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
        {
            pltlevel = PyString_FromString("ROW");
            PyDict_SetItemString(pltdata, "level", pltlevel);
            Py_DECREF(pltlevel);

            if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
            {
                pltevent = PyString_FromString("INSERT");

                PyDict_SetItemString(pltdata, "old", Py_None);
                pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
                                           tdata->tg_relation->rd_att);
                PyDict_SetItemString(pltdata, "new", pytnew);
                Py_DECREF(pytnew);
                *rv = tdata->tg_trigtuple;
            }
            else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
            {
                pltevent = PyString_FromString("DELETE");

                PyDict_SetItemString(pltdata, "new", Py_None);
                pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
                                           tdata->tg_relation->rd_att);
                PyDict_SetItemString(pltdata, "old", pytold);
                Py_DECREF(pytold);
                *rv = tdata->tg_trigtuple;
            }
            else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
            {
                pltevent = PyString_FromString("UPDATE");

                pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple,
                                           tdata->tg_relation->rd_att);
                PyDict_SetItemString(pltdata, "new", pytnew);
                Py_DECREF(pytnew);
                pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple,
                                           tdata->tg_relation->rd_att);
                PyDict_SetItemString(pltdata, "old", pytold);
                Py_DECREF(pytold);
                *rv = tdata->tg_newtuple;
            }
            else
            {
                elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
                pltevent = NULL;    /* keep compiler quiet */
            }

            PyDict_SetItemString(pltdata, "event", pltevent);
            Py_DECREF(pltevent);
        }
        else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event))
        {
            pltlevel = PyString_FromString("STATEMENT");
            PyDict_SetItemString(pltdata, "level", pltlevel);
            Py_DECREF(pltlevel);

            PyDict_SetItemString(pltdata, "old", Py_None);
            PyDict_SetItemString(pltdata, "new", Py_None);
            *rv = NULL;

            if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
                pltevent = PyString_FromString("INSERT");
            else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event))
                pltevent = PyString_FromString("DELETE");
            else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
                pltevent = PyString_FromString("UPDATE");
            else if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event))
                pltevent = PyString_FromString("TRUNCATE");
            else
            {
                elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event);
                pltevent = NULL;    /* keep compiler quiet */
            }

            PyDict_SetItemString(pltdata, "event", pltevent);
            Py_DECREF(pltevent);
        }
        else
            elog(ERROR, "unrecognized LEVEL tg_event: %u", tdata->tg_event);

        if (tdata->tg_trigger->tgnargs)
        {
            /*
             * all strings...
             */
            int         i;
            PyObject   *pltarg;

            pltargs = PyList_New(tdata->tg_trigger->tgnargs);
            for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
            {
                pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]);

                /*
                 * stolen, don't Py_DECREF
                 */
                PyList_SetItem(pltargs, i, pltarg);
            }
        }
        else
        {
            Py_INCREF(Py_None);
            pltargs = Py_None;
        }
        PyDict_SetItemString(pltdata, "args", pltargs);
        Py_DECREF(pltargs);
    }
    PG_CATCH();
    {
        Py_XDECREF(pltdata);
        PG_RE_THROW();
    }
    PG_END_TRY();

    return pltdata;
}