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);
81
* the other loop might throw an exception, if PLyTypeInfo member
82
* isn't properly initialized the Py_DECREF(plan) will go boom
84
for (i = 0; i < nargs; i++)
86
PLy_typeinfo_init(&plan->args[i]);
87
plan->values[i] = PointerGetDatum(NULL);
90
for (i = 0; i < nargs; i++)
96
Form_pg_type typeStruct;
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);
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
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
133
if (typeStruct->typtype != TYPTYPE_COMPOSITE)
134
PLy_output_datum_func(&plan->args[i], typeTup);
137
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
138
errmsg("plpy.prepare does not support composite types")));
139
ReleaseSysCache(typeTup);
142
pg_verifymbstr(query, strlen(query), false);
143
plan->plan = SPI_prepare(query, plan->nargs, plan->types);
144
if (plan->plan == NULL)
145
elog(ERROR, "SPI_prepare failed: %s",
146
SPI_result_code_string(SPI_result));
148
/* transfer plan from procCxt to topCxt */
149
if (SPI_keepplan(plan->plan))
150
elog(ERROR, "SPI_keepplan failed");
152
PLy_spi_subtransaction_commit(oldcontext, oldowner);
159
PLy_spi_subtransaction_abort(oldcontext, oldowner);
164
Assert(plan->plan != NULL);
165
return (PyObject *) plan;
168
/* execute(query="select * from foo", limit=5)
169
* execute(plan=plan, values=(foo, bar), limit=5)
172
PLy_spi_execute(PyObject *self, PyObject *args)
176
PyObject *list = NULL;
179
if (PyArg_ParseTuple(args, "s|l", &query, &limit))
180
return PLy_spi_execute_query(query, limit);
184
if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
185
is_PLyPlanObject(plan))
186
return PLy_spi_execute_plan(plan, list, limit);
188
PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
193
PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
199
volatile MemoryContext oldcontext;
200
volatile ResourceOwner oldowner;
205
if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
207
PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
210
nargs = PySequence_Length(list);
215
plan = (PLyPlanObject *) ob;
217
if (nargs != plan->nargs)
220
PyObject *so = PyObject_Str(list);
223
PLy_elog(ERROR, "could not execute plan");
224
sv = PyString_AsString(so);
225
PLy_exception_set_plural(PyExc_TypeError,
226
"Expected sequence of %d argument, got %d: %s",
227
"Expected sequence of %d arguments, got %d: %s",
229
plan->nargs, nargs, sv);
235
oldcontext = CurrentMemoryContext;
236
oldowner = CurrentResourceOwner;
238
PLy_spi_subtransaction_begin(oldcontext, oldowner);
242
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
243
char *volatile nulls;
247
nulls = palloc(nargs * sizeof(char));
251
for (j = 0; j < nargs; j++)
255
elem = PySequence_GetItem(list, j);
261
plan->args[j].out.d.func(&(plan->args[j].out.d),
279
InputFunctionCall(&(plan->args[j].out.d.typfunc),
281
plan->args[j].out.d.typioparam,
287
rv = SPI_execute_plan(plan->plan, plan->values, nulls,
288
exec_ctx->curr_proc->fn_readonly, limit);
289
ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
294
PLy_spi_subtransaction_commit(oldcontext, oldowner);
301
* cleanup plan->values array
303
for (k = 0; k < nargs; k++)
305
if (!plan->args[k].out.d.typbyval &&
306
(plan->values[k] != PointerGetDatum(NULL)))
308
pfree(DatumGetPointer(plan->values[k]));
309
plan->values[k] = PointerGetDatum(NULL);
313
PLy_spi_subtransaction_abort(oldcontext, oldowner);
318
for (i = 0; i < nargs; i++)
320
if (!plan->args[i].out.d.typbyval &&
321
(plan->values[i] != PointerGetDatum(NULL)))
323
pfree(DatumGetPointer(plan->values[i]));
324
plan->values[i] = PointerGetDatum(NULL);
330
PLy_exception_set(PLy_exc_spi_error,
331
"SPI_execute_plan failed: %s",
332
SPI_result_code_string(rv));
340
PLy_spi_execute_query(char *query, long limit)
343
volatile MemoryContext oldcontext;
344
volatile ResourceOwner oldowner;
345
PyObject *ret = NULL;
347
oldcontext = CurrentMemoryContext;
348
oldowner = CurrentResourceOwner;
350
PLy_spi_subtransaction_begin(oldcontext, oldowner);
354
PLyExecutionContext *exec_ctx = PLy_current_execution_context();
356
pg_verifymbstr(query, strlen(query), false);
357
rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
358
ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
360
PLy_spi_subtransaction_commit(oldcontext, oldowner);
364
PLy_spi_subtransaction_abort(oldcontext, oldowner);
372
PLy_exception_set(PLy_exc_spi_error,
373
"SPI_execute failed: %s",
374
SPI_result_code_string(rv));
382
PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
384
PLyResultObject *result;
385
volatile MemoryContext oldcontext;
387
result = (PLyResultObject *) PLy_result_new();
388
Py_DECREF(result->status);
389
result->status = PyInt_FromLong(status);
391
if (status > 0 && tuptable == NULL)
393
Py_DECREF(result->nrows);
394
result->nrows = PyInt_FromLong(rows);
396
else if (status > 0 && tuptable != NULL)
401
Py_DECREF(result->nrows);
402
result->nrows = PyInt_FromLong(rows);
403
PLy_typeinfo_init(&args);
405
oldcontext = CurrentMemoryContext;
408
MemoryContext oldcontext2;
411
* Save tuple descriptor for later use by result set metadata
412
* functions. Save it in TopMemoryContext so that it survives
413
* outside of an SPI context. We trust that PLy_result_dealloc()
414
* will clean it up when the time is right.
416
oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
417
result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
418
MemoryContextSwitchTo(oldcontext2);
422
Py_DECREF(result->rows);
423
result->rows = PyList_New(rows);
425
PLy_input_tuple_funcs(&args, tuptable->tupdesc);
426
for (i = 0; i < rows; i++)
428
PyObject *row = PLyDict_FromTuple(&args, tuptable->vals[i],
431
PyList_SetItem(result->rows, i, row);
437
MemoryContextSwitchTo(oldcontext);
438
if (!PyErr_Occurred())
439
PLy_exception_set(PLy_exc_error,
440
"unrecognized error in PLy_spi_execute_fetch_result");
441
PLy_typeinfo_dealloc(&args);
442
SPI_freetuptable(tuptable);
448
PLy_typeinfo_dealloc(&args);
449
SPI_freetuptable(tuptable);
452
return (PyObject *) result;
456
* Utilities for running SPI functions in subtransactions.
460
* MemoryContext oldcontext = CurrentMemoryContext;
461
* ResourceOwner oldowner = CurrentResourceOwner;
463
* PLy_spi_subtransaction_begin(oldcontext, oldowner);
466
* <call SPI functions>
467
* PLy_spi_subtransaction_commit(oldcontext, oldowner);
472
* PLy_spi_subtransaction_abort(oldcontext, oldowner);
477
* These utilities take care of restoring connection to the SPI manager and
478
* setting a Python exception in case of an abort.
481
PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
483
BeginInternalSubTransaction(NULL);
484
/* Want to run inside function's memory context */
485
MemoryContextSwitchTo(oldcontext);
489
PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
491
/* Commit the inner transaction, return to outer xact context */
492
ReleaseCurrentSubTransaction();
493
MemoryContextSwitchTo(oldcontext);
494
CurrentResourceOwner = oldowner;
497
* AtEOSubXact_SPI() should not have popped any SPI context, but just in
498
* case it did, make sure we remain connected.
500
SPI_restore_connection();
504
PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
507
PLyExceptionEntry *entry;
510
/* Save error info */
511
MemoryContextSwitchTo(oldcontext);
512
edata = CopyErrorData();
515
/* Abort the inner transaction */
516
RollbackAndReleaseCurrentSubTransaction();
517
MemoryContextSwitchTo(oldcontext);
518
CurrentResourceOwner = oldowner;
521
* If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
522
* have left us in a disconnected state. We need this hack to return to
525
SPI_restore_connection();
527
/* Look up the correct exception */
528
entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
530
/* We really should find it, but just in case have a fallback */
531
Assert(entry != NULL);
532
exc = entry ? entry->exc : PLy_exc_spi_error;
533
/* Make Python raise the exception */
534
PLy_spi_exception_set(exc, edata);
535
FreeErrorData(edata);
539
* Raise a SPIError, passing in it more error details, like the
540
* internal query and error position.
543
PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
545
PyObject *args = NULL;
546
PyObject *spierror = NULL;
547
PyObject *spidata = NULL;
549
args = Py_BuildValue("(s)", edata->message);
553
/* create a new SPI exception with the error message as the parameter */
554
spierror = PyObject_CallObject(excclass, args);
558
spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
559
edata->internalquery, edata->internalpos);
563
if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
566
PyErr_SetObject(excclass, spierror);
575
Py_XDECREF(spierror);
577
elog(ERROR, "could not convert SPI error to Python exception");