#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"
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) |
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] |
Definition at line 340 of file plpy_spi.c.
References PLyExecutionContext::curr_proc, CurrentMemoryContext, CurrentResourceOwner, PLyProcedure::fn_readonly, PG_CATCH, PG_END_TRY, PG_TRY, pg_verifymbstr(), PLy_current_execution_context(), PLy_exc_spi_error, PLy_exception_set(), PLy_spi_execute_fetch_result(), PLy_spi_subtransaction_abort(), PLy_spi_subtransaction_begin(), PLy_spi_subtransaction_commit(), SPI_execute(), SPI_processed, SPI_result_code_string(), and SPI_tuptable.
Referenced by PLy_spi_execute().
{ int rv; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; PyObject *ret = NULL; oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); pg_verifymbstr(query, strlen(query), false); rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit); ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); if (rv < 0) { Py_XDECREF(ret); PLy_exception_set(PLy_exc_spi_error, "SPI_execute failed: %s", SPI_result_code_string(rv)); return NULL; } return ret; }
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 | |||
) |
Definition at line 481 of file plpy_spi.c.
References BeginInternalSubTransaction(), MemoryContextSwitchTo(), and NULL.
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().
{ BeginInternalSubTransaction(NULL); /* Want to run inside function's memory context */ MemoryContextSwitchTo(oldcontext); }
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(); }