Age Owner TLA Line data Source code
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/htup_details.h"
12 : #include "access/xact.h"
13 : #include "catalog/pg_type.h"
14 : #include "executor/spi.h"
15 : #include "mb/pg_wchar.h"
16 : #include "parser/parse_type.h"
17 : #include "plpy_elog.h"
18 : #include "plpy_main.h"
19 : #include "plpy_planobject.h"
20 : #include "plpy_plpymodule.h"
21 : #include "plpy_procedure.h"
22 : #include "plpy_resultobject.h"
23 : #include "plpy_spi.h"
24 : #include "plpython.h"
25 : #include "utils/memutils.h"
26 : #include "utils/syscache.h"
27 :
28 : static PyObject *PLy_spi_execute_query(char *query, long limit);
29 : static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
30 : uint64 rows, int status);
31 : static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
32 :
33 :
34 : /* prepare(query="select * from foo")
35 : * prepare(query="select * from foo where bar = $1", params=["text"])
36 : * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
37 : */
38 : PyObject *
4130 peter_e 39 CBC 25 : PLy_spi_prepare(PyObject *self, PyObject *args)
40 : {
41 : PLyPlanObject *plan;
42 25 : PyObject *list = NULL;
43 25 : PyObject *volatile optr = NULL;
44 : char *query;
1970 tgl 45 25 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
46 : volatile MemoryContext oldcontext;
47 : volatile ResourceOwner oldowner;
48 : volatile int nargs;
49 :
2355 peter_e 50 25 : if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
4130 peter_e 51 UBC 0 : return NULL;
52 :
4130 peter_e 53 CBC 25 : if (list && (!PySequence_Check(list)))
54 : {
4130 peter_e 55 UBC 0 : PLy_exception_set(PyExc_TypeError,
56 : "second argument of plpy.prepare must be a sequence");
57 0 : return NULL;
58 : }
59 :
4130 peter_e 60 CBC 25 : if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
4130 peter_e 61 UBC 0 : return NULL;
62 :
2712 tgl 63 CBC 25 : plan->mcxt = AllocSetContextCreate(TopMemoryContext,
64 : "PL/Python plan context",
65 : ALLOCSET_DEFAULT_SIZES);
66 25 : oldcontext = MemoryContextSwitchTo(plan->mcxt);
67 :
4130 peter_e 68 25 : nargs = list ? PySequence_Length(list) : 0;
69 :
70 25 : plan->nargs = nargs;
1970 tgl 71 25 : plan->types = nargs ? palloc0(sizeof(Oid) * nargs) : NULL;
72 25 : plan->values = nargs ? palloc0(sizeof(Datum) * nargs) : NULL;
73 25 : plan->args = nargs ? palloc0(sizeof(PLyObToDatum) * nargs) : NULL;
74 :
2712 75 25 : MemoryContextSwitchTo(oldcontext);
76 :
4130 peter_e 77 25 : oldcontext = CurrentMemoryContext;
78 25 : oldowner = CurrentResourceOwner;
79 :
80 25 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
81 :
82 25 : PG_TRY();
83 : {
84 : int i;
85 :
86 39 : for (i = 0; i < nargs; i++)
87 : {
88 : char *sptr;
89 : Oid typeId;
90 : int32 typmod;
91 :
92 17 : optr = PySequence_GetItem(list, i);
398 andres 93 17 : if (PyUnicode_Check(optr))
4130 peter_e 94 17 : sptr = PLyUnicode_AsString(optr);
95 : else
96 : {
4130 peter_e 97 UBC 0 : ereport(ERROR,
98 : (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
99 : sptr = NULL; /* keep compiler quiet */
100 : }
101 :
102 : /********************************************************
103 : * Resolve argument type names and then look them up by
104 : * oid in the system cache, and remember the required
105 : *information for input conversion.
106 : ********************************************************/
107 :
103 tgl 108 GNC 17 : (void) parseTypeString(sptr, &typeId, &typmod, NULL);
109 :
4130 peter_e 110 CBC 14 : Py_DECREF(optr);
111 :
112 : /*
113 : * set optr to NULL, so we won't try to unref it again in case of
114 : * an error
115 : */
116 14 : optr = NULL;
117 :
118 14 : plan->types[i] = typeId;
1970 tgl 119 14 : PLy_output_setup_func(&plan->args[i], plan->mcxt,
120 : typeId, typmod,
121 14 : exec_ctx->curr_proc);
122 : }
123 :
4130 peter_e 124 22 : pg_verifymbstr(query, strlen(query), false);
125 22 : plan->plan = SPI_prepare(query, plan->nargs, plan->types);
126 22 : if (plan->plan == NULL)
4130 peter_e 127 UBC 0 : elog(ERROR, "SPI_prepare failed: %s",
128 : SPI_result_code_string(SPI_result));
129 :
130 : /* transfer plan from procCxt to topCxt */
4130 peter_e 131 CBC 22 : if (SPI_keepplan(plan->plan))
4130 peter_e 132 UBC 0 : elog(ERROR, "SPI_keepplan failed");
133 :
4130 peter_e 134 CBC 22 : PLy_spi_subtransaction_commit(oldcontext, oldowner);
135 : }
136 3 : PG_CATCH();
137 : {
138 3 : Py_DECREF(plan);
139 3 : Py_XDECREF(optr);
140 :
141 3 : PLy_spi_subtransaction_abort(oldcontext, oldowner);
142 3 : return NULL;
143 : }
144 22 : PG_END_TRY();
145 :
146 22 : Assert(plan->plan != NULL);
147 22 : return (PyObject *) plan;
148 : }
149 :
150 : /* execute(query="select * from foo", limit=5)
151 : * execute(plan=plan, values=(foo, bar), limit=5)
152 : */
153 : PyObject *
154 162 : PLy_spi_execute(PyObject *self, PyObject *args)
155 : {
156 : char *query;
157 : PyObject *plan;
158 162 : PyObject *list = NULL;
159 162 : long limit = 0;
160 :
161 162 : if (PyArg_ParseTuple(args, "s|l", &query, &limit))
162 141 : return PLy_spi_execute_query(query, limit);
163 :
164 21 : PyErr_Clear();
165 :
166 42 : if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
167 21 : is_PLyPlanObject(plan))
168 21 : return PLy_spi_execute_plan(plan, list, limit);
169 :
4130 peter_e 170 UBC 0 : PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
171 0 : return NULL;
172 : }
173 :
174 : PyObject *
4130 peter_e 175 CBC 22 : PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
176 : {
177 : volatile int nargs;
178 : int i,
179 : rv;
180 : PLyPlanObject *plan;
181 : volatile MemoryContext oldcontext;
182 : volatile ResourceOwner oldowner;
183 : PyObject *ret;
184 :
185 22 : if (list != NULL)
186 : {
398 andres 187 16 : if (!PySequence_Check(list) || PyUnicode_Check(list))
188 : {
4130 peter_e 189 UBC 0 : PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
190 0 : return NULL;
191 : }
4130 peter_e 192 CBC 16 : nargs = PySequence_Length(list);
193 : }
194 : else
195 6 : nargs = 0;
196 :
197 22 : plan = (PLyPlanObject *) ob;
198 :
199 22 : if (nargs != plan->nargs)
200 : {
201 : char *sv;
4130 peter_e 202 UBC 0 : PyObject *so = PyObject_Str(list);
203 :
204 0 : if (!so)
205 0 : PLy_elog(ERROR, "could not execute plan");
398 andres 206 0 : sv = PLyUnicode_AsString(so);
4130 peter_e 207 0 : PLy_exception_set_plural(PyExc_TypeError,
208 : "Expected sequence of %d argument, got %d: %s",
209 : "Expected sequence of %d arguments, got %d: %s",
210 0 : plan->nargs,
211 : plan->nargs, nargs, sv);
212 0 : Py_DECREF(so);
213 :
214 0 : return NULL;
215 : }
216 :
4130 peter_e 217 CBC 22 : oldcontext = CurrentMemoryContext;
218 22 : oldowner = CurrentResourceOwner;
219 :
220 22 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
221 :
222 22 : PG_TRY();
223 : {
4044 tgl 224 22 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
225 : char *volatile nulls;
226 : volatile int j;
227 :
4130 peter_e 228 22 : if (nargs > 0)
229 15 : nulls = palloc(nargs * sizeof(char));
230 : else
231 7 : nulls = NULL;
232 :
233 36 : for (j = 0; j < nargs; j++)
234 : {
1970 tgl 235 16 : PLyObToDatum *arg = &plan->args[j];
236 : PyObject *elem;
237 :
4130 peter_e 238 16 : elem = PySequence_GetItem(list, j);
184 drowley 239 GNC 16 : PG_TRY(2);
240 : {
241 : bool isnull;
242 :
1970 tgl 243 CBC 16 : plan->values[j] = PLy_output_convert(arg, elem, &isnull);
244 14 : nulls[j] = isnull ? 'n' : ' ';
245 : }
184 drowley 246 GNC 2 : PG_FINALLY(2);
247 : {
4130 peter_e 248 CBC 16 : Py_DECREF(elem);
249 : }
184 drowley 250 GNC 16 : PG_END_TRY(2);
251 : }
252 :
4130 peter_e 253 CBC 40 : rv = SPI_execute_plan(plan->plan, plan->values, nulls,
4044 tgl 254 20 : exec_ctx->curr_proc->fn_readonly, limit);
4130 peter_e 255 20 : ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
256 :
257 20 : if (nargs > 0)
258 13 : pfree(nulls);
259 :
260 20 : PLy_spi_subtransaction_commit(oldcontext, oldowner);
261 : }
262 2 : PG_CATCH();
263 : {
264 : int k;
265 :
266 : /*
267 : * cleanup plan->values array
268 : */
269 4 : for (k = 0; k < nargs; k++)
270 : {
1970 tgl 271 2 : if (!plan->args[k].typbyval &&
4130 peter_e 272 UBC 0 : (plan->values[k] != PointerGetDatum(NULL)))
273 : {
274 0 : pfree(DatumGetPointer(plan->values[k]));
275 0 : plan->values[k] = PointerGetDatum(NULL);
276 : }
277 : }
278 :
4130 peter_e 279 CBC 2 : PLy_spi_subtransaction_abort(oldcontext, oldowner);
280 2 : return NULL;
281 : }
282 20 : PG_END_TRY();
283 :
284 34 : for (i = 0; i < nargs; i++)
285 : {
1970 tgl 286 14 : if (!plan->args[i].typbyval &&
4130 peter_e 287 10 : (plan->values[i] != PointerGetDatum(NULL)))
288 : {
289 10 : pfree(DatumGetPointer(plan->values[i]));
290 10 : plan->values[i] = PointerGetDatum(NULL);
291 : }
292 : }
293 :
294 20 : if (rv < 0)
295 : {
296 1 : PLy_exception_set(PLy_exc_spi_error,
297 : "SPI_execute_plan failed: %s",
298 : SPI_result_code_string(rv));
299 1 : return NULL;
300 : }
301 :
302 19 : return ret;
303 : }
304 :
305 : static PyObject *
306 141 : PLy_spi_execute_query(char *query, long limit)
307 : {
308 : int rv;
309 : volatile MemoryContext oldcontext;
310 : volatile ResourceOwner oldowner;
4044 tgl 311 141 : PyObject *ret = NULL;
312 :
4130 peter_e 313 141 : oldcontext = CurrentMemoryContext;
314 141 : oldowner = CurrentResourceOwner;
315 :
316 141 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
317 :
318 141 : PG_TRY();
319 : {
3955 bruce 320 141 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
321 :
4130 peter_e 322 141 : pg_verifymbstr(query, strlen(query), false);
4044 tgl 323 141 : rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
4130 peter_e 324 120 : ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
325 :
326 120 : PLy_spi_subtransaction_commit(oldcontext, oldowner);
327 : }
328 21 : PG_CATCH();
329 : {
330 21 : PLy_spi_subtransaction_abort(oldcontext, oldowner);
331 21 : return NULL;
332 : }
333 120 : PG_END_TRY();
334 :
335 120 : if (rv < 0)
336 : {
4044 tgl 337 1 : Py_XDECREF(ret);
4130 peter_e 338 1 : PLy_exception_set(PLy_exc_spi_error,
339 : "SPI_execute failed: %s",
340 : SPI_result_code_string(rv));
341 1 : return NULL;
342 : }
343 :
344 119 : return ret;
345 : }
346 :
347 : static PyObject *
2584 tgl 348 140 : PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
349 : {
350 : PLyResultObject *result;
1970 351 140 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
352 : volatile MemoryContext oldcontext;
353 :
4130 peter_e 354 140 : result = (PLyResultObject *) PLy_result_new();
1986 355 140 : if (!result)
356 : {
1951 peter_e 357 UBC 0 : SPI_freetuptable(tuptable);
1986 358 0 : return NULL;
359 : }
4130 peter_e 360 CBC 140 : Py_DECREF(result->status);
398 andres 361 140 : result->status = PyLong_FromLong(status);
362 :
4130 peter_e 363 140 : if (status > 0 && tuptable == NULL)
364 : {
365 79 : Py_DECREF(result->nrows);
1905 366 79 : result->nrows = PyLong_FromUnsignedLongLong(rows);
367 : }
4130 368 61 : else if (status > 0 && tuptable != NULL)
369 : {
370 : PLyDatumToOb ininfo;
371 : MemoryContext cxt;
372 :
373 59 : Py_DECREF(result->nrows);
1905 374 59 : result->nrows = PyLong_FromUnsignedLongLong(rows);
375 :
2712 tgl 376 59 : cxt = AllocSetContextCreate(CurrentMemoryContext,
377 : "PL/Python temp context",
378 : ALLOCSET_DEFAULT_SIZES);
379 :
380 : /* Initialize for converting result tuples to Python */
1970 381 59 : PLy_input_setup_func(&ininfo, cxt, RECORDOID, -1,
382 59 : exec_ctx->curr_proc);
383 :
4130 peter_e 384 59 : oldcontext = CurrentMemoryContext;
385 59 : PG_TRY();
386 : {
387 : MemoryContext oldcontext2;
388 :
389 59 : 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 : */
2584 tgl 398 57 : if (rows > (uint64) PY_SSIZE_T_MAX)
2584 tgl 399 UBC 0 : ereport(ERROR,
400 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
401 : errmsg("query result has too many rows to fit in a Python list")));
402 :
4130 peter_e 403 CBC 57 : Py_DECREF(result->rows);
404 57 : result->rows = PyList_New(rows);
1951 405 57 : if (result->rows)
406 : {
1986 407 57 : PLy_input_setup_tuple(&ininfo, tuptable->tupdesc,
408 57 : exec_ctx->curr_proc);
409 :
410 131 : for (i = 0; i < rows; i++)
411 : {
412 148 : PyObject *row = PLy_input_from_tuple(&ininfo,
413 74 : tuptable->vals[i],
414 : tuptable->tupdesc,
415 : true);
416 :
417 74 : 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 : */
3550 tgl 430 59 : oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
431 59 : result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
432 59 : MemoryContextSwitchTo(oldcontext2);
433 : }
4130 peter_e 434 UBC 0 : PG_CATCH();
435 : {
436 0 : MemoryContextSwitchTo(oldcontext);
2712 tgl 437 0 : MemoryContextDelete(cxt);
4130 peter_e 438 0 : Py_DECREF(result);
3550 tgl 439 0 : PG_RE_THROW();
440 : }
4130 peter_e 441 CBC 59 : PG_END_TRY();
442 :
2712 tgl 443 59 : MemoryContextDelete(cxt);
4130 peter_e 444 59 : SPI_freetuptable(tuptable);
445 :
446 : /* in case PyList_New() failed above */
1951 447 59 : if (!result->rows)
448 : {
1951 peter_e 449 UBC 0 : Py_DECREF(result);
450 0 : result = NULL;
451 : }
452 : }
453 :
4130 peter_e 454 CBC 140 : return (PyObject *) result;
455 : }
456 :
457 : PyObject *
405 tgl 458 26 : PLy_commit(PyObject *self, PyObject *args)
459 : {
460 26 : MemoryContext oldcontext = CurrentMemoryContext;
461 26 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
462 :
463 26 : PG_TRY();
464 : {
465 26 : SPI_commit();
466 :
467 : /* was cleared at transaction end, reset pointer */
468 20 : exec_ctx->scratch_ctx = NULL;
469 : }
470 6 : PG_CATCH();
471 : {
472 : ErrorData *edata;
473 : PLyExceptionEntry *entry;
474 : PyObject *exc;
475 :
476 : /* Save error info */
477 6 : MemoryContextSwitchTo(oldcontext);
478 6 : edata = CopyErrorData();
479 6 : FlushErrorState();
480 :
481 : /* was cleared at transaction end, reset pointer */
482 6 : exec_ctx->scratch_ctx = NULL;
483 :
484 : /* Look up the correct exception */
485 6 : 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 6 : exc = entry ? entry->exc : PLy_exc_spi_error;
493 : /* Make Python raise the exception */
494 6 : PLy_spi_exception_set(exc, edata);
495 6 : FreeErrorData(edata);
496 :
497 6 : return NULL;
498 : }
499 20 : PG_END_TRY();
500 :
501 20 : Py_RETURN_NONE;
502 : }
503 :
504 : PyObject *
505 17 : PLy_rollback(PyObject *self, PyObject *args)
506 : {
507 17 : MemoryContext oldcontext = CurrentMemoryContext;
508 17 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
509 :
510 17 : PG_TRY();
511 : {
512 17 : SPI_rollback();
513 :
514 : /* was cleared at transaction end, reset pointer */
515 17 : exec_ctx->scratch_ctx = NULL;
516 : }
405 tgl 517 UBC 0 : PG_CATCH();
518 : {
519 : ErrorData *edata;
520 : PLyExceptionEntry *entry;
521 : PyObject *exc;
522 :
523 : /* Save error info */
524 0 : MemoryContextSwitchTo(oldcontext);
525 0 : edata = CopyErrorData();
526 0 : FlushErrorState();
527 :
528 : /* was cleared at transaction end, reset pointer */
529 0 : exec_ctx->scratch_ctx = NULL;
530 :
531 : /* Look up the correct exception */
532 0 : 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 0 : exc = entry ? entry->exc : PLy_exc_spi_error;
540 : /* Make Python raise the exception */
541 0 : PLy_spi_exception_set(exc, edata);
542 0 : FreeErrorData(edata);
543 :
544 0 : return NULL;
545 : }
405 tgl 546 CBC 17 : PG_END_TRY();
547 :
548 17 : Py_RETURN_NONE;
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 : */
576 : void
4130 peter_e 577 249 : PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
578 : {
579 249 : BeginInternalSubTransaction(NULL);
580 : /* Want to run inside function's memory context */
581 249 : MemoryContextSwitchTo(oldcontext);
582 249 : }
583 :
584 : void
585 223 : PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
586 : {
587 : /* Commit the inner transaction, return to outer xact context */
588 223 : ReleaseCurrentSubTransaction();
589 223 : MemoryContextSwitchTo(oldcontext);
590 223 : CurrentResourceOwner = oldowner;
591 223 : }
592 :
593 : void
594 26 : PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
595 : {
596 : ErrorData *edata;
597 : PLyExceptionEntry *entry;
598 : PyObject *exc;
599 :
600 : /* Save error info */
601 26 : MemoryContextSwitchTo(oldcontext);
602 26 : edata = CopyErrorData();
603 26 : FlushErrorState();
604 :
605 : /* Abort the inner transaction */
606 26 : RollbackAndReleaseCurrentSubTransaction();
607 26 : MemoryContextSwitchTo(oldcontext);
608 26 : CurrentResourceOwner = oldowner;
609 :
610 : /* Look up the correct exception */
611 26 : 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 26 : exc = entry ? entry->exc : PLy_exc_spi_error;
619 : /* Make Python raise the exception */
620 26 : PLy_spi_exception_set(exc, edata);
621 26 : FreeErrorData(edata);
622 26 : }
623 :
624 : /*
625 : * Raise a SPIError, passing in it more error details, like the
626 : * internal query and error position.
627 : */
628 : static void
629 32 : PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
630 : {
631 32 : PyObject *args = NULL;
632 32 : PyObject *spierror = NULL;
633 32 : PyObject *spidata = NULL;
634 :
635 32 : args = Py_BuildValue("(s)", edata->message);
636 32 : if (!args)
4130 peter_e 637 UBC 0 : goto failure;
638 :
639 : /* create a new SPI exception with the error message as the parameter */
4130 peter_e 640 CBC 32 : spierror = PyObject_CallObject(excclass, args);
641 32 : if (!spierror)
4130 peter_e 642 UBC 0 : goto failure;
643 :
2495 rhaas 644 CBC 32 : 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);
4130 peter_e 648 32 : if (!spidata)
4130 peter_e 649 UBC 0 : goto failure;
650 :
4130 peter_e 651 CBC 32 : if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
4130 peter_e 652 UBC 0 : goto failure;
653 :
4130 peter_e 654 CBC 32 : PyErr_SetObject(excclass, spierror);
655 :
656 32 : Py_DECREF(args);
657 32 : Py_DECREF(spierror);
658 32 : Py_DECREF(spidata);
659 32 : return;
660 :
4130 peter_e 661 UBC 0 : failure:
662 0 : Py_XDECREF(args);
663 0 : Py_XDECREF(spierror);
664 0 : Py_XDECREF(spidata);
665 0 : elog(ERROR, "could not convert SPI error to Python exception");
666 : }
|