2 * interface to SPI functions
4 * src/pl/plpython/plpy_spi.c
9 #include "access/htup_details.h"
10 #include "access/xact.h"
11 #include "catalog/pg_type.h"
12 #include "executor/spi.h"
13 #include "mb/pg_wchar.h"
14 #include "parser/parse_type.h"
15 #include "utils/memutils.h"
16 #include "utils/syscache.h"
22 #include "plpy_elog.h"
23 #include "plpy_main.h"
24 #include "plpy_planobject.h"
25 #include "plpy_plpymodule.h"
26 #include "plpy_procedure.h"
27 #include "plpy_resultobject.h"
30 static PyObject *PLy_spi_execute_query(char *query, long limit);
31 static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
32 static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status);
33 static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
36 /* prepare(query="select * from foo")
37 * prepare(query="select * from foo where bar = $1", params=["text"])
38 * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
41 PLy_spi_prepare(PyObject *self, PyObject *args)
44 PyObject *list = NULL;
45 PyObject *volatile optr = NULL;
47 volatile MemoryContext oldcontext;
48 volatile ResourceOwner oldowner;
51 if (!PyArg_ParseTuple(args, "s|O", &query, &list))
54 if (list && (!PySequence_Check(list)))
56 PLy_exception_set(PyExc_TypeError,
57 "second argument of plpy.prepare must be a sequence");
61 if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
64 nargs = list ? PySequence_Length(list) : 0;
67 plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL;
68 plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL;
69 plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
71 oldcontext = CurrentMemoryContext;
72 oldowner = CurrentResourceOwner;
74 PLy_spi_subtransaction_begin(oldcontext, oldowner);
79 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
82 * the other loop might throw an exception, if PLyTypeInfo member
83 * isn't properly initialized the Py_DECREF(plan) will go boom
85 for (i = 0; i < nargs; i++)
87 PLy_typeinfo_init(&plan->args[i]);
88 plan->values[i] = PointerGetDatum(NULL);
91 for (i = 0; i < nargs; i++)
98 optr = PySequence_GetItem(list, i);
99 if (PyString_Check(optr))
100 sptr = PyString_AsString(optr);
101 else if (PyUnicode_Check(optr))
102 sptr = PLyUnicode_AsString(optr);
106 (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
107 sptr = NULL; /* keep compiler quiet */
110 /********************************************************
111 * Resolve argument type names and then look them up by
112 * oid in the system cache, and remember the required
113 *information for input conversion.
114 ********************************************************/
116 parseTypeString(sptr, &typeId, &typmod, false);
118 typeTup = SearchSysCache1(TYPEOID,
119 ObjectIdGetDatum(typeId));
120 if (!HeapTupleIsValid(typeTup))
121 elog(ERROR, "cache lookup failed for type %u", typeId);
126 * set optr to NULL, so we won't try to unref it again in case of
131 plan->types[i] = typeId;
132 PLy_output_datum_func(&plan->args[i], typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes);
133 ReleaseSysCache(typeTup);
136 pg_verifymbstr(query, strlen(query), false);
137 plan->plan = SPI_prepare(query, plan->nargs, plan->types);
138 if (plan->plan == NULL)
139 elog(ERROR, "SPI_prepare failed: %s",
140 SPI_result_code_string(SPI_result));
142 /* transfer plan from procCxt to topCxt */
143 if (SPI_keepplan(plan->plan))
144 elog(ERROR, "SPI_keepplan failed");
146 PLy_spi_subtransaction_commit(oldcontext, oldowner);
153 PLy_spi_subtransaction_abort(oldcontext, oldowner);
158 Assert(plan->plan != NULL);
159 return (PyObject *) plan;
162 /* execute(query="select * from foo", limit=5)
163 * execute(plan=plan, values=(foo, bar), limit=5)
166 PLy_spi_execute(PyObject *self, PyObject *args)
170 PyObject *list = NULL;
173 if (PyArg_ParseTuple(args, "s|l", &query, &limit))
174 return PLy_spi_execute_query(query, limit);
178 if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
179 is_PLyPlanObject(plan))
180 return PLy_spi_execute_plan(plan, list, limit);
182 PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
187 PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
193 volatile MemoryContext oldcontext;
194 volatile ResourceOwner oldowner;
199 if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
201 PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
204 nargs = PySequence_Length(list);
209 plan = (PLyPlanObject *) ob;
211 if (nargs != plan->nargs)
214 PyObject *so = PyObject_Str(list);
217 PLy_elog(ERROR, "could not execute plan");
218 sv = PyString_AsString(so);
219 PLy_exception_set_plural(PyExc_TypeError,
220 "Expected sequence of %d argument, got %d: %s",
221 "Expected sequence of %d arguments, got %d: %s",
223 plan->nargs, nargs, sv);
229 oldcontext = CurrentMemoryContext;
230 oldowner = CurrentResourceOwner;
232 PLy_spi_subtransaction_begin(oldcontext, oldowner);
236 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
237 char *volatile nulls;
241 nulls = palloc(nargs * sizeof(char));
245 for (j = 0; j < nargs; j++)
249 elem = PySequence_GetItem(list, j);
255 plan->args[j].out.d.func(&(plan->args[j].out.d),
273 InputFunctionCall(&(plan->args[j].out.d.typfunc),
275 plan->args[j].out.d.typioparam,
281 rv = SPI_execute_plan(plan->plan, plan->values, nulls,
282 exec_ctx->curr_proc->fn_readonly, limit);
283 ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
288 PLy_spi_subtransaction_commit(oldcontext, oldowner);
295 * cleanup plan->values array
297 for (k = 0; k < nargs; k++)
299 if (!plan->args[k].out.d.typbyval &&
300 (plan->values[k] != PointerGetDatum(NULL)))
302 pfree(DatumGetPointer(plan->values[k]));
303 plan->values[k] = PointerGetDatum(NULL);
307 PLy_spi_subtransaction_abort(oldcontext, oldowner);
312 for (i = 0; i < nargs; i++)
314 if (!plan->args[i].out.d.typbyval &&
315 (plan->values[i] != PointerGetDatum(NULL)))
317 pfree(DatumGetPointer(plan->values[i]));
318 plan->values[i] = PointerGetDatum(NULL);
324 PLy_exception_set(PLy_exc_spi_error,
325 "SPI_execute_plan failed: %s",
326 SPI_result_code_string(rv));
334 PLy_spi_execute_query(char *query, long limit)
337 volatile MemoryContext oldcontext;
338 volatile ResourceOwner oldowner;
339 PyObject *ret = NULL;
341 oldcontext = CurrentMemoryContext;
342 oldowner = CurrentResourceOwner;
344 PLy_spi_subtransaction_begin(oldcontext, oldowner);
348 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
350 pg_verifymbstr(query, strlen(query), false);
351 rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
352 ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
354 PLy_spi_subtransaction_commit(oldcontext, oldowner);
358 PLy_spi_subtransaction_abort(oldcontext, oldowner);
366 PLy_exception_set(PLy_exc_spi_error,
367 "SPI_execute failed: %s",
368 SPI_result_code_string(rv));
376 PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
378 PLyResultObject *result;
379 volatile MemoryContext oldcontext;
381 result = (PLyResultObject *) PLy_result_new();
382 Py_DECREF(result->status);
383 result->status = PyInt_FromLong(status);
385 if (status > 0 && tuptable == NULL)
387 Py_DECREF(result->nrows);
388 result->nrows = PyInt_FromLong(rows);
390 else if (status > 0 && tuptable != NULL)
395 Py_DECREF(result->nrows);
396 result->nrows = PyInt_FromLong(rows);
397 PLy_typeinfo_init(&args);
399 oldcontext = CurrentMemoryContext;
402 MemoryContext oldcontext2;
406 Py_DECREF(result->rows);
407 result->rows = PyList_New(rows);
409 PLy_input_tuple_funcs(&args, tuptable->tupdesc);
410 for (i = 0; i < rows; i++)
412 PyObject *row = PLyDict_FromTuple(&args,
416 PyList_SetItem(result->rows, i, row);
421 * Save tuple descriptor for later use by result set metadata
422 * functions. Save it in TopMemoryContext so that it survives
423 * outside of an SPI context. We trust that PLy_result_dealloc()
424 * will clean it up when the time is right. (Do this as late as
425 * possible, to minimize the number of ways the tupdesc could get
426 * leaked due to errors.)
428 oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
429 result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
430 MemoryContextSwitchTo(oldcontext2);
434 MemoryContextSwitchTo(oldcontext);
435 PLy_typeinfo_dealloc(&args);
441 PLy_typeinfo_dealloc(&args);
442 SPI_freetuptable(tuptable);
445 return (PyObject *) result;
449 * Utilities for running SPI functions in subtransactions.
453 * MemoryContext oldcontext = CurrentMemoryContext;
454 * ResourceOwner oldowner = CurrentResourceOwner;
456 * PLy_spi_subtransaction_begin(oldcontext, oldowner);
459 * <call SPI functions>
460 * PLy_spi_subtransaction_commit(oldcontext, oldowner);
465 * PLy_spi_subtransaction_abort(oldcontext, oldowner);
470 * These utilities take care of restoring connection to the SPI manager and
471 * setting a Python exception in case of an abort.
474 PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
476 BeginInternalSubTransaction(NULL);
477 /* Want to run inside function's memory context */
478 MemoryContextSwitchTo(oldcontext);
482 PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
484 /* Commit the inner transaction, return to outer xact context */
485 ReleaseCurrentSubTransaction();
486 MemoryContextSwitchTo(oldcontext);
487 CurrentResourceOwner = oldowner;
490 * AtEOSubXact_SPI() should not have popped any SPI context, but just in
491 * case it did, make sure we remain connected.
493 SPI_restore_connection();
497 PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
500 PLyExceptionEntry *entry;
503 /* Save error info */
504 MemoryContextSwitchTo(oldcontext);
505 edata = CopyErrorData();
508 /* Abort the inner transaction */
509 RollbackAndReleaseCurrentSubTransaction();
510 MemoryContextSwitchTo(oldcontext);
511 CurrentResourceOwner = oldowner;
514 * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
515 * have left us in a disconnected state. We need this hack to return to
518 SPI_restore_connection();
520 /* Look up the correct exception */
521 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
523 /* We really should find it, but just in case have a fallback */
524 Assert(entry != NULL);
525 exc = entry ? entry->exc : PLy_exc_spi_error;
526 /* Make Python raise the exception */
527 PLy_spi_exception_set(exc, edata);
528 FreeErrorData(edata);
532 * Raise a SPIError, passing in it more error details, like the
533 * internal query and error position.
536 PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
538 PyObject *args = NULL;
539 PyObject *spierror = NULL;
540 PyObject *spidata = NULL;
542 args = Py_BuildValue("(s)", edata->message);
546 /* create a new SPI exception with the error message as the parameter */
547 spierror = PyObject_CallObject(excclass, args);
551 spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
552 edata->internalquery, edata->internalpos);
556 if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
559 PyErr_SetObject(excclass, spierror);
568 Py_XDECREF(spierror);
570 elog(ERROR, "could not convert SPI error to Python exception");