Header And Logo

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

Functions

plpy_spi.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "mb/pg_wchar.h"
#include "parser/parse_type.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "plpython.h"
#include "plpy_spi.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_planobject.h"
#include "plpy_plpymodule.h"
#include "plpy_procedure.h"
#include "plpy_resultobject.h"
Include dependency graph for plpy_spi.c:

Go to the source code of this file.

Functions

static PyObject * PLy_spi_execute_query (char *query, long limit)
static PyObject * PLy_spi_execute_plan (PyObject *ob, PyObject *list, long limit)
static PyObject * PLy_spi_execute_fetch_result (SPITupleTable *tuptable, int rows, int status)
static void PLy_spi_exception_set (PyObject *excclass, ErrorData *edata)
PyObject * PLy_spi_prepare (PyObject *self, PyObject *args)
PyObject * PLy_spi_execute (PyObject *self, PyObject *args)
void PLy_spi_subtransaction_begin (MemoryContext oldcontext, ResourceOwner oldowner)
void PLy_spi_subtransaction_commit (MemoryContext oldcontext, ResourceOwner oldowner)
void PLy_spi_subtransaction_abort (MemoryContext oldcontext, ResourceOwner oldowner)

Function Documentation

static void PLy_spi_exception_set ( PyObject *  excclass,
ErrorData edata 
) [static]

Definition at line 543 of file plpy_spi.c.

References ErrorData::detail, elog, ERROR, ErrorData::hint, ErrorData::internalpos, ErrorData::internalquery, ErrorData::message, and ErrorData::sqlerrcode.

Referenced by PLy_spi_subtransaction_abort().

{
    PyObject   *args = NULL;
    PyObject   *spierror = NULL;
    PyObject   *spidata = NULL;

    args = Py_BuildValue("(s)", edata->message);
    if (!args)
        goto failure;

    /* create a new SPI exception with the error message as the parameter */
    spierror = PyObject_CallObject(excclass, args);
    if (!spierror)
        goto failure;

    spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
                            edata->internalquery, edata->internalpos);
    if (!spidata)
        goto failure;

    if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
        goto failure;

    PyErr_SetObject(excclass, spierror);

    Py_DECREF(args);
    Py_DECREF(spierror);
    Py_DECREF(spidata);
    return;

failure:
    Py_XDECREF(args);
    Py_XDECREF(spierror);
    Py_XDECREF(spidata);
    elog(ERROR, "could not convert SPI error to Python exception");
}

PyObject* PLy_spi_execute ( PyObject *  self,
PyObject *  args 
)

Definition at line 172 of file plpy_spi.c.

References is_PLyPlanObject(), sort-test::list, PLy_exc_error, PLy_exception_set(), PLy_spi_execute_plan(), and PLy_spi_execute_query().

{
    char       *query;
    PyObject   *plan;
    PyObject   *list = NULL;
    long        limit = 0;

    if (PyArg_ParseTuple(args, "s|l", &query, &limit))
        return PLy_spi_execute_query(query, limit);

    PyErr_Clear();

    if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
        is_PLyPlanObject(plan))
        return PLy_spi_execute_plan(plan, list, limit);

    PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
    return NULL;
}

static PyObject * PLy_spi_execute_fetch_result ( SPITupleTable tuptable,
int  rows,
int  status 
) [static]

Definition at line 382 of file plpy_spi.c.

References CreateTupleDescCopy(), CurrentMemoryContext, i, MemoryContextSwitchTo(), PLyResultObject::nrows, NULL, PG_CATCH, PG_END_TRY, PG_TRY, PLy_exc_error, PLy_exception_set(), PLy_input_tuple_funcs(), PLy_result_new(), PLy_typeinfo_dealloc(), PLy_typeinfo_init(), PLyDict_FromTuple(), PLyResultObject::rows, SPI_freetuptable(), PLyResultObject::status, TopMemoryContext, SPITupleTable::tupdesc, PLyResultObject::tupdesc, and SPITupleTable::vals.

Referenced by PLy_spi_execute_plan(), and PLy_spi_execute_query().

{
    PLyResultObject *result;
    volatile MemoryContext oldcontext;

    result = (PLyResultObject *) PLy_result_new();
    Py_DECREF(result->status);
    result->status = PyInt_FromLong(status);

    if (status > 0 && tuptable == NULL)
    {
        Py_DECREF(result->nrows);
        result->nrows = PyInt_FromLong(rows);
    }
    else if (status > 0 && tuptable != NULL)
    {
        PLyTypeInfo args;
        int         i;

        Py_DECREF(result->nrows);
        result->nrows = PyInt_FromLong(rows);
        PLy_typeinfo_init(&args);

        oldcontext = CurrentMemoryContext;
        PG_TRY();
        {
            MemoryContext oldcontext2;

            /*
             * Save tuple descriptor for later use by result set metadata
             * functions.  Save it in TopMemoryContext so that it survives
             * outside of an SPI context.  We trust that PLy_result_dealloc()
             * will clean it up when the time is right.
             */
            oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
            result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
            MemoryContextSwitchTo(oldcontext2);

            if (rows)
            {
                Py_DECREF(result->rows);
                result->rows = PyList_New(rows);

                PLy_input_tuple_funcs(&args, tuptable->tupdesc);
                for (i = 0; i < rows; i++)
                {
                    PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
                                                        tuptable->tupdesc);

                    PyList_SetItem(result->rows, i, row);
                }
            }
        }
        PG_CATCH();
        {
            MemoryContextSwitchTo(oldcontext);
            if (!PyErr_Occurred())
                PLy_exception_set(PLy_exc_error,
                       "unrecognized error in PLy_spi_execute_fetch_result");
            PLy_typeinfo_dealloc(&args);
            SPI_freetuptable(tuptable);
            Py_DECREF(result);
            return NULL;
        }
        PG_END_TRY();

        PLy_typeinfo_dealloc(&args);
        SPI_freetuptable(tuptable);
    }

    return (PyObject *) result;
}

static PyObject * PLy_spi_execute_plan ( PyObject *  ob,
PyObject *  list,
long  limit 
) [static]

Definition at line 193 of file plpy_spi.c.

References PLyPlanObject::args, PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, PLyTypeOutput::d, DatumGetPointer, ERROR, PLyProcedure::fn_readonly, PLyObToDatum::func, i, InputFunctionCall(), PLyPlanObject::nargs, NULL, PLyTypeInfo::out, palloc(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLyPlanObject::plan, PLy_current_execution_context(), PLy_elog(), PLy_exc_spi_error, PLy_exception_set(), PLy_exception_set_plural(), PLy_spi_execute_fetch_result(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), PointerGetDatum, SPI_execute_plan(), SPI_processed, SPI_result_code_string(), SPI_tuptable, PLyObToDatum::typbyval, PLyObToDatum::typfunc, PLyObToDatum::typioparam, and PLyPlanObject::values.

Referenced by PLy_spi_execute().

{
    volatile int nargs;
    int         i,
                rv;
    PLyPlanObject *plan;
    volatile MemoryContext oldcontext;
    volatile ResourceOwner oldowner;
    PyObject   *ret;

    if (list != NULL)
    {
        if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
        {
            PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
            return NULL;
        }
        nargs = PySequence_Length(list);
    }
    else
        nargs = 0;

    plan = (PLyPlanObject *) ob;

    if (nargs != plan->nargs)
    {
        char       *sv;
        PyObject   *so = PyObject_Str(list);

        if (!so)
            PLy_elog(ERROR, "could not execute plan");
        sv = PyString_AsString(so);
        PLy_exception_set_plural(PyExc_TypeError,
                              "Expected sequence of %d argument, got %d: %s",
                             "Expected sequence of %d arguments, got %d: %s",
                                 plan->nargs,
                                 plan->nargs, nargs, sv);
        Py_DECREF(so);

        return NULL;
    }

    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;

    PLy_spi_subtransaction_begin(oldcontext, oldowner);

    PG_TRY();
    {
        PLyExecutionContext *exec_ctx = PLy_current_execution_context();
        char       *volatile nulls;
        volatile int j;

        if (nargs > 0)
            nulls = palloc(nargs * sizeof(char));
        else
            nulls = NULL;

        for (j = 0; j < nargs; j++)
        {
            PyObject   *elem;

            elem = PySequence_GetItem(list, j);
            if (elem != Py_None)
            {
                PG_TRY();
                {
                    plan->values[j] =
                        plan->args[j].out.d.func(&(plan->args[j].out.d),
                                                 -1,
                                                 elem);
                }
                PG_CATCH();
                {
                    Py_DECREF(elem);
                    PG_RE_THROW();
                }
                PG_END_TRY();

                Py_DECREF(elem);
                nulls[j] = ' ';
            }
            else
            {
                Py_DECREF(elem);
                plan->values[j] =
                    InputFunctionCall(&(plan->args[j].out.d.typfunc),
                                      NULL,
                                      plan->args[j].out.d.typioparam,
                                      -1);
                nulls[j] = 'n';
            }
        }

        rv = SPI_execute_plan(plan->plan, plan->values, nulls,
                              exec_ctx->curr_proc->fn_readonly, limit);
        ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);

        if (nargs > 0)
            pfree(nulls);

        PLy_spi_subtransaction_commit(oldcontext, oldowner);
    }
    PG_CATCH();
    {
        int         k;

        /*
         * cleanup plan->values array
         */
        for (k = 0; k < nargs; k++)
        {
            if (!plan->args[k].out.d.typbyval &&
                (plan->values[k] != PointerGetDatum(NULL)))
            {
                pfree(DatumGetPointer(plan->values[k]));
                plan->values[k] = PointerGetDatum(NULL);
            }
        }

        PLy_spi_subtransaction_abort(oldcontext, oldowner);
        return NULL;
    }
    PG_END_TRY();

    for (i = 0; i < nargs; i++)
    {
        if (!plan->args[i].out.d.typbyval &&
            (plan->values[i] != PointerGetDatum(NULL)))
        {
            pfree(DatumGetPointer(plan->values[i]));
            plan->values[i] = PointerGetDatum(NULL);
        }
    }

    if (rv < 0)
    {
        PLy_exception_set(PLy_exc_spi_error,
                          "SPI_execute_plan failed: %s",
                          SPI_result_code_string(rv));
        return NULL;
    }

    return ret;
}

static PyObject * PLy_spi_execute_query ( char *  query,
long  limit 
) [static]
PyObject* PLy_spi_prepare ( PyObject *  self,
PyObject *  args 
)

Definition at line 41 of file plpy_spi.c.

References PLyPlanObject::args, Assert, CurrentMemoryContext, CurrentResourceOwner, elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, i, sort-test::list, PLyPlanObject::nargs, NULL, ObjectIdGetDatum, parseTypeString(), PG_CATCH, PG_END_TRY, PG_TRY, pg_verifymbstr(), PLyPlanObject::plan, PLy_exception_set(), PLy_malloc(), PLy_output_datum_func(), PLy_plan_new(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), PLy_typeinfo_init(), PLyUnicode_AsString(), PointerGetDatum, ReleaseSysCache(), SearchSysCache1, SPI_keepplan(), SPI_prepare(), SPI_result, SPI_result_code_string(), TYPEOID, PLyPlanObject::types, TYPTYPE_COMPOSITE, and PLyPlanObject::values.

{
    PLyPlanObject *plan;
    PyObject   *list = NULL;
    PyObject   *volatile optr = NULL;
    char       *query;
    volatile MemoryContext oldcontext;
    volatile ResourceOwner oldowner;
    volatile int nargs;

    if (!PyArg_ParseTuple(args, "s|O", &query, &list))
        return NULL;

    if (list && (!PySequence_Check(list)))
    {
        PLy_exception_set(PyExc_TypeError,
                       "second argument of plpy.prepare must be a sequence");
        return NULL;
    }

    if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
        return NULL;

    nargs = list ? PySequence_Length(list) : 0;

    plan->nargs = nargs;
    plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL;
    plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL;
    plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;

    oldcontext = CurrentMemoryContext;
    oldowner = CurrentResourceOwner;

    PLy_spi_subtransaction_begin(oldcontext, oldowner);

    PG_TRY();
    {
        int         i;

        /*
         * the other loop might throw an exception, if PLyTypeInfo member
         * isn't properly initialized the Py_DECREF(plan) will go boom
         */
        for (i = 0; i < nargs; i++)
        {
            PLy_typeinfo_init(&plan->args[i]);
            plan->values[i] = PointerGetDatum(NULL);
        }

        for (i = 0; i < nargs; i++)
        {
            char       *sptr;
            HeapTuple   typeTup;
            Oid         typeId;
            int32       typmod;
            Form_pg_type typeStruct;

            optr = PySequence_GetItem(list, i);
            if (PyString_Check(optr))
                sptr = PyString_AsString(optr);
            else if (PyUnicode_Check(optr))
                sptr = PLyUnicode_AsString(optr);
            else
            {
                ereport(ERROR,
                        (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
                sptr = NULL;    /* keep compiler quiet */
            }

            /********************************************************
             * Resolve argument type names and then look them up by
             * oid in the system cache, and remember the required
             *information for input conversion.
             ********************************************************/

            parseTypeString(sptr, &typeId, &typmod);

            typeTup = SearchSysCache1(TYPEOID,
                                      ObjectIdGetDatum(typeId));
            if (!HeapTupleIsValid(typeTup))
                elog(ERROR, "cache lookup failed for type %u", typeId);

            Py_DECREF(optr);

            /*
             * set optr to NULL, so we won't try to unref it again in case of
             * an error
             */
            optr = NULL;

            plan->types[i] = typeId;
            typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
            if (typeStruct->typtype != TYPTYPE_COMPOSITE)
                PLy_output_datum_func(&plan->args[i], typeTup);
            else
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                   errmsg("plpy.prepare does not support composite types")));
            ReleaseSysCache(typeTup);
        }

        pg_verifymbstr(query, strlen(query), false);
        plan->plan = SPI_prepare(query, plan->nargs, plan->types);
        if (plan->plan == NULL)
            elog(ERROR, "SPI_prepare failed: %s",
                 SPI_result_code_string(SPI_result));

        /* transfer plan from procCxt to topCxt */
        if (SPI_keepplan(plan->plan))
            elog(ERROR, "SPI_keepplan failed");

        PLy_spi_subtransaction_commit(oldcontext, oldowner);
    }
    PG_CATCH();
    {
        Py_DECREF(plan);
        Py_XDECREF(optr);

        PLy_spi_subtransaction_abort(oldcontext, oldowner);
        return NULL;
    }
    PG_END_TRY();

    Assert(plan->plan != NULL);
    return (PyObject *) plan;
}

void PLy_spi_subtransaction_abort ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)

Definition at line 504 of file plpy_spi.c.

References Assert, CopyErrorData(), CurrentResourceOwner, PLyExceptionEntry::exc, FlushErrorState(), FreeErrorData(), HASH_FIND, hash_search(), MemoryContextSwitchTo(), NULL, PLy_exc_spi_error, PLy_spi_exception_set(), PLy_spi_exceptions, RollbackAndReleaseCurrentSubTransaction(), SPI_restore_connection(), and ErrorData::sqlerrcode.

Referenced by PLy_cursor_fetch(), PLy_cursor_iternext(), PLy_cursor_plan(), PLy_cursor_query(), PLy_spi_execute_plan(), PLy_spi_execute_query(), and PLy_spi_prepare().

{
    ErrorData  *edata;
    PLyExceptionEntry *entry;
    PyObject   *exc;

    /* Save error info */
    MemoryContextSwitchTo(oldcontext);
    edata = CopyErrorData();
    FlushErrorState();

    /* Abort the inner transaction */
    RollbackAndReleaseCurrentSubTransaction();
    MemoryContextSwitchTo(oldcontext);
    CurrentResourceOwner = oldowner;

    /*
     * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
     * have left us in a disconnected state.  We need this hack to return to
     * connected state.
     */
    SPI_restore_connection();

    /* Look up the correct exception */
    entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
                        HASH_FIND, NULL);
    /* We really should find it, but just in case have a fallback */
    Assert(entry != NULL);
    exc = entry ? entry->exc : PLy_exc_spi_error;
    /* Make Python raise the exception */
    PLy_spi_exception_set(exc, edata);
    FreeErrorData(edata);
}

void PLy_spi_subtransaction_begin ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)
void PLy_spi_subtransaction_commit ( MemoryContext  oldcontext,
ResourceOwner  oldowner 
)

Definition at line 489 of file plpy_spi.c.

References CurrentResourceOwner, MemoryContextSwitchTo(), ReleaseCurrentSubTransaction(), and SPI_restore_connection().

Referenced by PLy_cursor_fetch(), PLy_cursor_iternext(), PLy_cursor_plan(), PLy_cursor_query(), PLy_spi_execute_plan(), PLy_spi_execute_query(), and PLy_spi_prepare().

{
    /* Commit the inner transaction, return to outer xact context */
    ReleaseCurrentSubTransaction();
    MemoryContextSwitchTo(oldcontext);
    CurrentResourceOwner = oldowner;

    /*
     * AtEOSubXact_SPI() should not have popped any SPI context, but just in
     * case it did, make sure we remain connected.
     */
    SPI_restore_connection();
}