Header And Logo

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

Functions

plpy_exec.h File Reference

#include "plpy_procedure.h"
Include dependency graph for plpy_exec.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

Datum PLy_exec_function (FunctionCallInfo fcinfo, PLyProcedure *proc)
HeapTuple PLy_exec_trigger (FunctionCallInfo fcinfo, PLyProcedure *proc)

Function Documentation

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;
}