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);
92 else
93 {
95 (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
96 sptr = NULL; /* keep compiler quiet */
97 }
98
99 /********************************************************
100 * Resolve argument type names and then look them up by
101 * oid in the system cache, and remember the required
102 *information for input conversion.
103 ********************************************************/
104
105 (void) parseTypeString(sptr, &typeId, &typmod, NULL);
106
108
109 /*
110 * set optr to NULL, so we won't try to unref it again in case of
111 * an error
112 */
113 optr = NULL;
114
115 plan->types[i] = typeId;
116 PLy_output_setup_func(&plan->args[i], plan->mcxt,
117 typeId, typmod,
118 exec_ctx->curr_proc);
119 }
120
121 pg_verifymbstr(query, strlen(query), false);
122 plan->plan = SPI_prepare(query, plan->nargs, plan->types);
123 if (plan->plan == NULL)
124 elog(ERROR, "SPI_prepare failed: %s",
126
127 /* transfer plan from procCxt to topCxt */
128 if (SPI_keepplan(plan->plan))
129 elog(ERROR, "SPI_keepplan failed");
130
131 PLy_spi_subtransaction_commit(oldcontext, oldowner);
132 }
133 PG_CATCH();
134 {
137
138 PLy_spi_subtransaction_abort(oldcontext, oldowner);
139 return NULL;
140 }
141 PG_END_TRY();
142
143 Assert(plan->plan != NULL);
144 return (PyObject *) plan;
145}
146
147/*
148 * execute(query="select * from foo", limit=5)
149 * execute(plan=plan, values=(foo, bar), limit=5)
150 */
151PyObject *
153{
154 char *query;
155 PyObject *plan;
156 PyObject *list = NULL;
157 long limit = 0;
158
159 if (PyArg_ParseTuple(args, "s|l", &query, &limit))
160 return PLy_spi_execute_query(query, limit);
161
162 PyErr_Clear();
163
164 if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
166 return PLy_spi_execute_plan(plan, list, limit);
167
168 PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
169 return NULL;
170}
171
172PyObject *
174{
175 volatile int nargs;
176 int rv;
178 volatile MemoryContext oldcontext;
179 volatile ResourceOwner oldowner;
180 PyObject *ret;
181
182 if (list != NULL)
183 {
184 if (!PySequence_Check(list) || PyUnicode_Check(list))
185 {
186 PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
187 return NULL;
188 }
189 nargs = PySequence_Length(list);
190 }
191 else
192 nargs = 0;
193
194 plan = (PLyPlanObject *) ob;
195
196 if (nargs != plan->nargs)
197 {
198 char *sv;
199 PyObject *so = PyObject_Str(list);
200
201 if (!so)
202 PLy_elog(ERROR, "could not execute plan");
205 "Expected sequence of %d argument, got %d: %s",
206 "Expected sequence of %d arguments, got %d: %s",
207 plan->nargs,
208 plan->nargs, nargs, sv);
209 Py_DECREF(so);
210
211 return NULL;
212 }
213
214 oldcontext = CurrentMemoryContext;
215 oldowner = CurrentResourceOwner;
216
217 PLy_spi_subtransaction_begin(oldcontext, oldowner);
218
219 PG_TRY();
220 {
222 MemoryContext tmpcontext;
223 Datum *volatile values;
224 char *volatile nulls;
225 volatile int j;
226
227 /*
228 * Converted arguments and associated cruft will be in this context,
229 * which is local to our subtransaction.
230 */
232 "PL/Python temporary context",
234 MemoryContextSwitchTo(tmpcontext);
235
236 if (nargs > 0)
237 {
238 values = (Datum *) palloc(nargs * sizeof(Datum));
239 nulls = (char *) palloc(nargs * sizeof(char));
240 }
241 else
242 {
243 values = NULL;
244 nulls = NULL;
245 }
246
247 for (j = 0; j < nargs; j++)
248 {
249 PLyObToDatum *arg = &plan->args[j];
250 PyObject *elem;
251
252 elem = PySequence_GetItem(list, j);
253 PG_TRY(2);
254 {
255 bool isnull;
256
257 values[j] = PLy_output_convert(arg, elem, &isnull);
258 nulls[j] = isnull ? 'n' : ' ';
259 }
260 PG_FINALLY(2);
261 {
262 Py_DECREF(elem);
263 }
264 PG_END_TRY(2);
265 }
266
267 MemoryContextSwitchTo(oldcontext);
268
269 rv = SPI_execute_plan(plan->plan, values, nulls,
270 exec_ctx->curr_proc->fn_readonly, limit);
272
273 MemoryContextDelete(tmpcontext);
274 PLy_spi_subtransaction_commit(oldcontext, oldowner);
275 }
276 PG_CATCH();
277 {
278 /* Subtransaction abort will remove the tmpcontext */
279 PLy_spi_subtransaction_abort(oldcontext, oldowner);
280 return NULL;
281 }
282 PG_END_TRY();
283
284 if (rv < 0)
285 {
287 "SPI_execute_plan failed: %s",
289 return NULL;
290 }
291
292 return ret;
293}
294
295static PyObject *
296PLy_spi_execute_query(char *query, long limit)
297{
298 int rv;
299 volatile MemoryContext oldcontext;
300 volatile ResourceOwner oldowner;
301 PyObject *ret = NULL;
302
303 oldcontext = CurrentMemoryContext;
304 oldowner = CurrentResourceOwner;
305
306 PLy_spi_subtransaction_begin(oldcontext, oldowner);
307
308 PG_TRY();
309 {
311
312 pg_verifymbstr(query, strlen(query), false);
313 rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
315
316 PLy_spi_subtransaction_commit(oldcontext, oldowner);
317 }
318 PG_CATCH();
319 {
320 PLy_spi_subtransaction_abort(oldcontext, oldowner);
321 return NULL;
322 }
323 PG_END_TRY();
324
325 if (rv < 0)
326 {
327 Py_XDECREF(ret);
329 "SPI_execute failed: %s",
331 return NULL;
332 }
333
334 return ret;
335}
336
337static PyObject *
339{
342 volatile MemoryContext oldcontext;
343
345 if (!result)
346 {
347 SPI_freetuptable(tuptable);
348 return NULL;
349 }
350 Py_DECREF(result->status);
351 result->status = PyLong_FromLong(status);
352
353 if (status > 0 && tuptable == NULL)
354 {
355 Py_DECREF(result->nrows);
356 result->nrows = PyLong_FromUnsignedLongLong(rows);
357 }
358 else if (status > 0 && tuptable != NULL)
359 {
361 MemoryContext cxt;
362
363 Py_DECREF(result->nrows);
364 result->nrows = PyLong_FromUnsignedLongLong(rows);
365
367 "PL/Python temp context",
369
370 /* Initialize for converting result tuples to Python */
372 exec_ctx->curr_proc);
373
374 oldcontext = CurrentMemoryContext;
375 PG_TRY();
376 {
378
379 if (rows)
380 {
381 uint64 i;
382
383 /*
384 * PyList_New() and PyList_SetItem() use Py_ssize_t for list
385 * size and list indices; so we cannot support a result larger
386 * than PY_SSIZE_T_MAX.
387 */
388 if (rows > (uint64) PY_SSIZE_T_MAX)
391 errmsg("query result has too many rows to fit in a Python list")));
392
393 Py_DECREF(result->rows);
394 result->rows = PyList_New(rows);
395 if (result->rows)
396 {
398 exec_ctx->curr_proc);
399
400 for (i = 0; i < rows; i++)
401 {
403 tuptable->vals[i],
404 tuptable->tupdesc,
405 true);
406
407 PyList_SetItem(result->rows, i, row);
408 }
409 }
410 }
411
412 /*
413 * Save tuple descriptor for later use by result set metadata
414 * functions. Save it in TopMemoryContext so that it survives
415 * outside of an SPI context. We trust that PLy_result_dealloc()
416 * will clean it up when the time is right. (Do this as late as
417 * possible, to minimize the number of ways the tupdesc could get
418 * leaked due to errors.)
419 */
421 result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
423 }
424 PG_CATCH();
425 {
426 MemoryContextSwitchTo(oldcontext);
429 PG_RE_THROW();
430 }
431 PG_END_TRY();
432
434 SPI_freetuptable(tuptable);
435
436 /* in case PyList_New() failed above */
437 if (!result->rows)
438 {
440 result = NULL;
441 }
442 }
443
444 return (PyObject *) result;
445}
446
447PyObject *
449{
452
453 PG_TRY();
454 {
455 SPI_commit();
456
457 /* was cleared at transaction end, reset pointer */
458 exec_ctx->scratch_ctx = NULL;
459 }
460 PG_CATCH();
461 {
463 PLyExceptionEntry *entry;
464 PyObject *exc;
465
466 /* Save error info */
467 MemoryContextSwitchTo(oldcontext);
470
471 /* was cleared at transaction end, reset pointer */
472 exec_ctx->scratch_ctx = NULL;
473
474 /* Look up the correct exception */
475 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
476 HASH_FIND, NULL);
477
478 /*
479 * This could be a custom error code, if that's the case fallback to
480 * SPIError
481 */
482 exc = entry ? entry->exc : PLy_exc_spi_error;
483 /* Make Python raise the exception */
486
487 return NULL;
488 }
489 PG_END_TRY();
490
492}
493
494PyObject *
496{
499
500 PG_TRY();
501 {
502 SPI_rollback();
503
504 /* was cleared at transaction end, reset pointer */
505 exec_ctx->scratch_ctx = NULL;
506 }
507 PG_CATCH();
508 {
510 PLyExceptionEntry *entry;
511 PyObject *exc;
512
513 /* Save error info */
514 MemoryContextSwitchTo(oldcontext);
517
518 /* was cleared at transaction end, reset pointer */
519 exec_ctx->scratch_ctx = NULL;
520
521 /* Look up the correct exception */
522 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
523 HASH_FIND, NULL);
524
525 /*
526 * This could be a custom error code, if that's the case fallback to
527 * SPIError
528 */
529 exc = entry ? entry->exc : PLy_exc_spi_error;
530 /* Make Python raise the exception */
533
534 return NULL;
535 }
536 PG_END_TRY();
537
539}
540
541/*
542 * Utilities for running SPI functions in subtransactions.
543 *
544 * Usage:
545 *
546 * MemoryContext oldcontext = CurrentMemoryContext;
547 * ResourceOwner oldowner = CurrentResourceOwner;
548 *
549 * PLy_spi_subtransaction_begin(oldcontext, oldowner);
550 * PG_TRY();
551 * {
552 * <call SPI functions>
553 * PLy_spi_subtransaction_commit(oldcontext, oldowner);
554 * }
555 * PG_CATCH();
556 * {
557 * <do cleanup>
558 * PLy_spi_subtransaction_abort(oldcontext, oldowner);
559 * return NULL;
560 * }
561 * PG_END_TRY();
562 *
563 * These utilities take care of restoring connection to the SPI manager and
564 * setting a Python exception in case of an abort.
565 */
566void
568{
570 /* Want to run inside function's memory context */
571 MemoryContextSwitchTo(oldcontext);
572}
573
574void
576{
577 /* Commit the inner transaction, return to outer xact context */
579 MemoryContextSwitchTo(oldcontext);
580 CurrentResourceOwner = oldowner;
581}
582
583void
585{
587 PLyExceptionEntry *entry;
588 PyObject *exc;
589
590 /* Save error info */
591 MemoryContextSwitchTo(oldcontext);
594
595 /* Abort the inner transaction */
597 MemoryContextSwitchTo(oldcontext);
598 CurrentResourceOwner = oldowner;
599
600 /* Look up the correct exception */
601 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
602 HASH_FIND, NULL);
603
604 /*
605 * This could be a custom error code, if that's the case fallback to
606 * SPIError
607 */
608 exc = entry ? entry->exc : PLy_exc_spi_error;
609 /* Make Python raise the exception */
612}
613
614/*
615 * Raise a SPIError, passing in it more error details, like the
616 * internal query and error position.
617 */
618static void
620{
621 PyObject *args = NULL;
624
625 args = Py_BuildValue("(s)", edata->message);
626 if (!args)
627 goto failure;
628
629 /* create a new SPI exception with the error message as the parameter */
631 if (!spierror)
632 goto failure;
633
634 spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
635 edata->internalquery, edata->internalpos,
636 edata->schema_name, edata->table_name, edata->column_name,
637 edata->datatype_name, edata->constraint_name);
638 if (!spidata)
639 goto failure;
640
641 if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
642 goto failure;
643
645
646 Py_DECREF(args);
649 return;
650
651failure:
652 Py_XDECREF(args);
655 elog(ERROR, "could not convert SPI error to Python exception");
656}
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define Assert(condition)
Definition c.h:943
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
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:336
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:575
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:338
PyObject * PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
Definition plpy_spi.c:173
PyObject * PLy_commit(PyObject *self, PyObject *args)
Definition plpy_spi.c:448
PyObject * PLy_rollback(PyObject *self, PyObject *args)
Definition plpy_spi.c:495
static PyObject * PLy_spi_execute_query(char *query, long limit)
Definition plpy_spi.c:296
PyObject * PLy_spi_execute(PyObject *self, PyObject *args)
Definition plpy_spi.c:152
void PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
Definition plpy_spi.c:584
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
Definition plpy_spi.c:619
void PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
Definition plpy_spi.c:567
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
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition spi.c:861
int SPI_keepplan(SPIPlanPtr plan)
Definition spi.c:977
void SPI_rollback(void)
Definition spi.c:414
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:4745
void RollbackAndReleaseCurrentSubTransaction(void)
Definition xact.c:4847
void ReleaseCurrentSubTransaction(void)
Definition xact.c:4819