PostgreSQL Source Code git master
Loading...
Searching...
No Matches
plpy_spi.c
Go to the documentation of this file.
1/*
2 * interface to SPI functions
3 *
4 * src/pl/plpython/plpy_spi.c
5 */
6
7#include "postgres.h"
8
9#include <limits.h>
10
11#include "access/xact.h"
12#include "catalog/pg_type.h"
13#include "executor/spi.h"
14#include "mb/pg_wchar.h"
15#include "parser/parse_type.h"
16#include "plpy_elog.h"
17#include "plpy_main.h"
18#include "plpy_planobject.h"
19#include "plpy_plpymodule.h"
20#include "plpy_resultobject.h"
21#include "plpy_spi.h"
22#include "plpy_util.h"
23#include "utils/memutils.h"
24
25static PyObject *PLy_spi_execute_query(char *query, long limit);
27 uint64 rows, int status);
29
30
31/*
32 * prepare(query="select * from foo")
33 * prepare(query="select * from foo where bar = $1", params=["text"])
34 * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
35 */
38{
40 PyObject *list = NULL;
41 PyObject *volatile optr = NULL;
42 char *query;
44 volatile MemoryContext oldcontext;
45 volatile ResourceOwner oldowner;
46 volatile int nargs;
47
48 if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
49 return NULL;
50
51 if (list && (!PySequence_Check(list)))
52 {
54 "second argument of plpy.prepare must be a sequence");
55 return NULL;
56 }
57
58 if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
59 return NULL;
60
62 "PL/Python plan context",
64 oldcontext = MemoryContextSwitchTo(plan->mcxt);
65
66 nargs = list ? PySequence_Length(list) : 0;
67
68 plan->nargs = nargs;
69 plan->types = nargs ? palloc0_array(Oid, nargs) : NULL;
70 plan->args = nargs ? palloc0_array(PLyObToDatum, nargs) : NULL;
71
72 MemoryContextSwitchTo(oldcontext);
73
74 oldcontext = CurrentMemoryContext;
75 oldowner = CurrentResourceOwner;
76
77 PLy_spi_subtransaction_begin(oldcontext, oldowner);
78
79 PG_TRY();
80 {
81 int i;
82
83 for (i = 0; i < nargs; i++)
84 {
85 char *sptr;
86 Oid typeId;
87 int32 typmod;
88
89 optr = PySequence_GetItem(list, i);
90
91 /* PySequence_GetItem() can return NULL, with an exception set */
92 if (optr == NULL)
93 PLy_elog(ERROR, "could not get element %d from sequence", i);
94
97 else
98 {
100 (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
101 sptr = NULL; /* keep compiler quiet */
102 }
103
104 /********************************************************
105 * Resolve argument type names and then look them up by
106 * oid in the system cache, and remember the required
107 *information for input conversion.
108 ********************************************************/
109
110 (void) parseTypeString(sptr, &typeId, &typmod, NULL);
111
113
114 /*
115 * set optr to NULL, so we won't try to unref it again in case of
116 * an error
117 */
118 optr = NULL;
119
120 plan->types[i] = typeId;
121 PLy_output_setup_func(&plan->args[i], plan->mcxt,
122 typeId, typmod,
123 exec_ctx->curr_proc);
124 }
125
126 pg_verifymbstr(query, strlen(query), false);
127 plan->plan = SPI_prepare(query, plan->nargs, plan->types);
128 if (plan->plan == NULL)
129 elog(ERROR, "SPI_prepare failed: %s",
131
132 /* transfer plan from procCxt to topCxt */
133 if (SPI_keepplan(plan->plan))
134 elog(ERROR, "SPI_keepplan failed");
135
136 PLy_spi_subtransaction_commit(oldcontext, oldowner);
137 }
138 PG_CATCH();
139 {
142
143 PLy_spi_subtransaction_abort(oldcontext, oldowner);
144 return NULL;
145 }
146 PG_END_TRY();
147
148 Assert(plan->plan != NULL);
149 return (PyObject *) plan;
150}
151
152/*
153 * execute(query="select * from foo", limit=5)
154 * execute(plan=plan, values=(foo, bar), limit=5)
155 */
156PyObject *
158{
159 char *query;
160 PyObject *plan;
161 PyObject *list = NULL;
162 long limit = 0;
163
164 if (PyArg_ParseTuple(args, "s|l", &query, &limit))
165 return PLy_spi_execute_query(query, limit);
166
167 PyErr_Clear();
168
169 if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
171 return PLy_spi_execute_plan(plan, list, limit);
172
173 PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
174 return NULL;
175}
176
177PyObject *
179{
180 volatile int nargs;
181 int rv;
183 volatile MemoryContext oldcontext;
184 volatile ResourceOwner oldowner;
185 PyObject *ret;
186
187 if (list != NULL)
188 {
189 if (!PySequence_Check(list) || PyUnicode_Check(list))
190 {
191 PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
192 return NULL;
193 }
194 nargs = PySequence_Length(list);
195 }
196 else
197 nargs = 0;
198
199 plan = (PLyPlanObject *) ob;
200
201 if (nargs != plan->nargs)
202 {
203 char *sv;
204 PyObject *so = PyObject_Str(list);
205
206 if (!so)
207 PLy_elog(ERROR, "could not execute plan");
210 "Expected sequence of %d argument, got %d: %s",
211 "Expected sequence of %d arguments, got %d: %s",
212 plan->nargs,
213 plan->nargs, nargs, sv);
214 Py_DECREF(so);
215
216 return NULL;
217 }
218
219 oldcontext = CurrentMemoryContext;
220 oldowner = CurrentResourceOwner;
221
222 PLy_spi_subtransaction_begin(oldcontext, oldowner);
223
224 PG_TRY();
225 {
227 MemoryContext tmpcontext;
228 Datum *volatile values;
229 char *volatile nulls;
230 volatile int j;
231
232 /*
233 * Converted arguments and associated cruft will be in this context,
234 * which is local to our subtransaction.
235 */
237 "PL/Python temporary context",
239 MemoryContextSwitchTo(tmpcontext);
240
241 if (nargs > 0)
242 {
243 values = (Datum *) palloc(nargs * sizeof(Datum));
244 nulls = (char *) palloc(nargs * sizeof(char));
245 }
246 else
247 {
248 values = NULL;
249 nulls = NULL;
250 }
251
252 for (j = 0; j < nargs; j++)
253 {
254 PLyObToDatum *arg = &plan->args[j];
255 PyObject *elem;
256
257 elem = PySequence_GetItem(list, j);
258
259 /* PySequence_GetItem() can return NULL, with an exception set */
260 if (elem == NULL)
261 PLy_elog(ERROR, "could not get element %d from sequence", j);
262
263 PG_TRY(2);
264 {
265 bool isnull;
266
267 values[j] = PLy_output_convert(arg, elem, &isnull);
268 nulls[j] = isnull ? 'n' : ' ';
269 }
270 PG_FINALLY(2);
271 {
272 Py_DECREF(elem);
273 }
274 PG_END_TRY(2);
275 }
276
277 MemoryContextSwitchTo(oldcontext);
278
279 rv = SPI_execute_plan(plan->plan, values, nulls,
280 exec_ctx->curr_proc->fn_readonly, limit);
282
283 MemoryContextDelete(tmpcontext);
284 PLy_spi_subtransaction_commit(oldcontext, oldowner);
285 }
286 PG_CATCH();
287 {
288 /* Subtransaction abort will remove the tmpcontext */
289 PLy_spi_subtransaction_abort(oldcontext, oldowner);
290 return NULL;
291 }
292 PG_END_TRY();
293
294 if (rv < 0)
295 {
297 "SPI_execute_plan failed: %s",
299 return NULL;
300 }
301
302 return ret;
303}
304
305static PyObject *
306PLy_spi_execute_query(char *query, long limit)
307{
308 int rv;
309 volatile MemoryContext oldcontext;
310 volatile ResourceOwner oldowner;
311 PyObject *ret = NULL;
312
313 oldcontext = CurrentMemoryContext;
314 oldowner = CurrentResourceOwner;
315
316 PLy_spi_subtransaction_begin(oldcontext, oldowner);
317
318 PG_TRY();
319 {
321
322 pg_verifymbstr(query, strlen(query), false);
323 rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
325
326 PLy_spi_subtransaction_commit(oldcontext, oldowner);
327 }
328 PG_CATCH();
329 {
330 PLy_spi_subtransaction_abort(oldcontext, oldowner);
331 return NULL;
332 }
333 PG_END_TRY();
334
335 if (rv < 0)
336 {
337 Py_XDECREF(ret);
339 "SPI_execute failed: %s",
341 return NULL;
342 }
343
344 return ret;
345}
346
347static PyObject *
349{
352 volatile MemoryContext oldcontext;
353
355 if (!result)
356 {
357 SPI_freetuptable(tuptable);
358 return NULL;
359 }
360 Py_DECREF(result->status);
361 result->status = PyLong_FromLong(status);
362
363 if (status > 0 && tuptable == NULL)
364 {
365 Py_DECREF(result->nrows);
366 result->nrows = PyLong_FromUnsignedLongLong(rows);
367 }
368 else if (status > 0 && tuptable != NULL)
369 {
371 MemoryContext cxt;
372
373 Py_DECREF(result->nrows);
374 result->nrows = PyLong_FromUnsignedLongLong(rows);
375
377 "PL/Python temp context",
379
380 /* Initialize for converting result tuples to Python */
382 exec_ctx->curr_proc);
383
384 oldcontext = CurrentMemoryContext;
385 PG_TRY();
386 {
388
389 if (rows)
390 {
391 uint64 i;
392
393 /*
394 * PyList_New() and PyList_SetItem() use Py_ssize_t for list
395 * size and list indices; so we cannot support a result larger
396 * than PY_SSIZE_T_MAX.
397 */
398 if (rows > (uint64) PY_SSIZE_T_MAX)
401 errmsg("query result has too many rows to fit in a Python list")));
402
403 Py_DECREF(result->rows);
404 result->rows = PyList_New(rows);
405 if (result->rows)
406 {
408 exec_ctx->curr_proc);
409
410 for (i = 0; i < rows; i++)
411 {
413 tuptable->vals[i],
414 tuptable->tupdesc,
415 true);
416
417 PyList_SetItem(result->rows, i, row);
418 }
419 }
420 }
421
422 /*
423 * Save tuple descriptor for later use by result set metadata
424 * functions. Save it in TopMemoryContext so that it survives
425 * outside of an SPI context. We trust that PLy_result_dealloc()
426 * will clean it up when the time is right. (Do this as late as
427 * possible, to minimize the number of ways the tupdesc could get
428 * leaked due to errors.)
429 */
431 result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
433 }
434 PG_CATCH();
435 {
436 MemoryContextSwitchTo(oldcontext);
439 PG_RE_THROW();
440 }
441 PG_END_TRY();
442
444 SPI_freetuptable(tuptable);
445
446 /* in case PyList_New() failed above */
447 if (!result->rows)
448 {
450 result = NULL;
451 }
452 }
453
454 return (PyObject *) result;
455}
456
457PyObject *
459{
462
463 PG_TRY();
464 {
465 SPI_commit();
466
467 /* was cleared at transaction end, reset pointer */
468 exec_ctx->scratch_ctx = NULL;
469 }
470 PG_CATCH();
471 {
473 PLyExceptionEntry *entry;
474 PyObject *exc;
475
476 /* Save error info */
477 MemoryContextSwitchTo(oldcontext);
480
481 /* was cleared at transaction end, reset pointer */
482 exec_ctx->scratch_ctx = NULL;
483
484 /* Look up the correct exception */
485 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
486 HASH_FIND, NULL);
487
488 /*
489 * This could be a custom error code, if that's the case fallback to
490 * SPIError
491 */
492 exc = entry ? entry->exc : PLy_exc_spi_error;
493 /* Make Python raise the exception */
496
497 return NULL;
498 }
499 PG_END_TRY();
500
502}
503
504PyObject *
506{
509
510 PG_TRY();
511 {
512 SPI_rollback();
513
514 /* was cleared at transaction end, reset pointer */
515 exec_ctx->scratch_ctx = NULL;
516 }
517 PG_CATCH();
518 {
520 PLyExceptionEntry *entry;
521 PyObject *exc;
522
523 /* Save error info */
524 MemoryContextSwitchTo(oldcontext);
527
528 /* was cleared at transaction end, reset pointer */
529 exec_ctx->scratch_ctx = NULL;
530
531 /* Look up the correct exception */
532 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
533 HASH_FIND, NULL);
534
535 /*
536 * This could be a custom error code, if that's the case fallback to
537 * SPIError
538 */
539 exc = entry ? entry->exc : PLy_exc_spi_error;
540 /* Make Python raise the exception */
543
544 return NULL;
545 }
546 PG_END_TRY();
547
549}
550
551/*
552 * Utilities for running SPI functions in subtransactions.
553 *
554 * Usage:
555 *
556 * MemoryContext oldcontext = CurrentMemoryContext;
557 * ResourceOwner oldowner = CurrentResourceOwner;
558 *
559 * PLy_spi_subtransaction_begin(oldcontext, oldowner);
560 * PG_TRY();
561 * {
562 * <call SPI functions>
563 * PLy_spi_subtransaction_commit(oldcontext, oldowner);
564 * }
565 * PG_CATCH();
566 * {
567 * <do cleanup>
568 * PLy_spi_subtransaction_abort(oldcontext, oldowner);
569 * return NULL;
570 * }
571 * PG_END_TRY();
572 *
573 * These utilities take care of restoring connection to the SPI manager and
574 * setting a Python exception in case of an abort.
575 */
576void
578{
580 /* Want to run inside function's memory context */
581 MemoryContextSwitchTo(oldcontext);
582}
583
584void
586{
587 /* Commit the inner transaction, return to outer xact context */
589 MemoryContextSwitchTo(oldcontext);
590 CurrentResourceOwner = oldowner;
591}
592
593void
595{
597 PLyExceptionEntry *entry;
598 PyObject *exc;
599
600 /* Save error info */
601 MemoryContextSwitchTo(oldcontext);
604
605 /* Abort the inner transaction */
607 MemoryContextSwitchTo(oldcontext);
608 CurrentResourceOwner = oldowner;
609
610 /* Look up the correct exception */
611 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
612 HASH_FIND, NULL);
613
614 /*
615 * This could be a custom error code, if that's the case fallback to
616 * SPIError
617 */
618 exc = entry ? entry->exc : PLy_exc_spi_error;
619 /* Make Python raise the exception */
622}
623
624/*
625 * Raise a SPIError, passing in it more error details, like the
626 * internal query and error position.
627 */
628static void
630{
631 PyObject *args = NULL;
634
635 args = Py_BuildValue("(s)", edata->message);
636 if (!args)
637 goto failure;
638
639 /* create a new SPI exception with the error message as the parameter */
641 if (!spierror)
642 goto failure;
643
644 spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
645 edata->internalquery, edata->internalpos,
646 edata->schema_name, edata->table_name, edata->column_name,
647 edata->datatype_name, edata->constraint_name);
648 if (!spidata)
649 goto failure;
650
651 if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
652 goto failure;
653
655
656 Py_DECREF(args);
659 return;
660
661failure:
662 Py_XDECREF(args);
665 elog(ERROR, "could not convert SPI error to Python exception");
666}
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define Assert(condition)
Definition c.h:999
int32_t int32
Definition c.h:676
uint64_t uint64
Definition c.h:681
uint32 result
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
Datum arg
Definition elog.c:1323
void FreeErrorData(ErrorData *edata)
Definition elog.c:2014
ErrorData * CopyErrorData(void)
Definition elog.c:1942
void FlushErrorState(void)
Definition elog.c:2063
int errcode(int sqlerrcode)
Definition elog.c:875
#define PG_RE_THROW()
Definition elog.h:407
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define ERROR
Definition elog.h:40
#define PG_CATCH(...)
Definition elog.h:384
#define elog(elevel,...)
Definition elog.h:228
#define PG_FINALLY(...)
Definition elog.h:391
#define ereport(elevel,...)
Definition elog.h:152
#define palloc0_array(type, count)
Definition fe_memutils.h:92
@ HASH_FIND
Definition hsearch.h:108
int j
Definition isn.c:78
int i
Definition isn.c:77
#define PLy_elog
bool pg_verifymbstr(const char *mbstr, int len, bool noError)
Definition mbutils.c:1683
MemoryContext TopMemoryContext
Definition mcxt.c:167
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurTransactionContext
Definition mcxt.c:173
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
bool parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, Node *escontext)
Definition parse_type.c:785
#define plan(x)
Definition pg_regress.c:164
PyObject * PLy_exc_error
Definition plpy_elog.c:15
PyObject * PLy_exc_spi_error
Definition plpy_elog.c:17
void PLy_exception_set(PyObject *exc, const char *fmt,...)
Definition plpy_elog.c:490
void PLy_exception_set_plural(PyObject *exc, const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition plpy_elog.c:504
PLyExecutionContext * PLy_current_execution_context(void)
Definition plpy_main.c:383
PyObject * PLy_plan_new(void)
bool is_PLyPlanObject(PyObject *ob)
HTAB * PLy_spi_exceptions
PyObject * PLy_result_new(void)
void PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
Definition plpy_spi.c:585
PyObject * PLy_spi_prepare(PyObject *self, PyObject *args)
Definition plpy_spi.c:37
static PyObject * PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
Definition plpy_spi.c:348
PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
Definition plpy_spi.c:178
PyObject * PLy_commit(PyObject *self, PyObject *args)
Definition plpy_spi.c:458
PyObject * PLy_rollback(PyObject *self, PyObject *args)
Definition plpy_spi.c:505
static PyObject * PLy_spi_execute_query(char *query, long limit)
Definition plpy_spi.c:306
PyObject * PLy_spi_execute(PyObject *self, PyObject *args)
Definition plpy_spi.c:157
void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
Definition plpy_spi.c:594
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
Definition plpy_spi.c:629
void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition plpy_spi.c:577
void PLy_output_setup_func(PLyObToDatum *arg, MemoryContext arg_mcxt, Oid typeOid, int32 typmod, PLyProcedure *proc)
void PLy_input_setup_func(PLyDatumToOb *arg, MemoryContext arg_mcxt, Oid typeOid, int32 typmod, PLyProcedure *proc)
PyObject * PLy_input_from_tuple(PLyDatumToOb *arg, HeapTuple tuple, TupleDesc desc, bool include_generated)
void PLy_input_setup_tuple(PLyDatumToOb *arg, TupleDesc desc, PLyProcedure *proc)
Datum PLy_output_convert(PLyObToDatum *arg, PyObject *val, bool *isnull)
char * PLyUnicode_AsString(PyObject *unicode)
Definition plpy_util.c:81
uint64_t Datum
Definition postgres.h:70
unsigned int Oid
static int fb(int x)
ResourceOwner CurrentResourceOwner
Definition resowner.c:173
void SPI_commit(void)
Definition spi.c:321
uint64 SPI_processed
Definition spi.c:45
const char * SPI_result_code_string(int code)
Definition spi.c:1973
SPITupleTable * SPI_tuptable
Definition spi.c:46
int SPI_execute_plan(SPIPlanPtr plan, const Datum *Values, const char *Nulls, bool read_only, long tcount)
Definition spi.c:673
int SPI_result
Definition spi.c:47
void SPI_freetuptable(SPITupleTable *tuptable)
Definition spi.c:1387
int SPI_keepplan(SPIPlanPtr plan)
Definition spi.c:977
void SPI_rollback(void)
Definition spi.c:414
SPIPlanPtr SPI_prepare(const char *src, int nargs, const Oid *argtypes)
Definition spi.c:861
int SPI_execute(const char *src, bool read_only, long tcount)
Definition spi.c:597
PyObject * exc
Definition plpy_spi.h:21
TupleDesc tupdesc
Definition spi.h:25
HeapTuple * vals
Definition spi.h:26
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
void BeginInternalSubTransaction(const char *name)
Definition xact.c:4748
void RollbackAndReleaseCurrentSubTransaction(void)
Definition xact.c:4850
void ReleaseCurrentSubTransaction(void)
Definition xact.c:4822