Bug Summary

File:plpy_spi.c
Location:line 444, column 20
Description:Access to field 'tupdesc' results in a dereference of a null pointer (loaded from variable 'result')

Annotated 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 "utils/memutils.h"
18#include "utils/syscache.h"
19
20#include "plpython.h"
21
22#include "plpy_spi.h"
23
24#include "plpy_elog.h"
25#include "plpy_main.h"
26#include "plpy_planobject.h"
27#include "plpy_plpymodule.h"
28#include "plpy_procedure.h"
29#include "plpy_resultobject.h"
30
31
32static PyObject *PLy_spi_execute_query(char *query, long limit);
33static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable,
34 uint64 rows, int status);
35static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
36
37
38/* prepare(query="select * from foo")
39 * prepare(query="select * from foo where bar = $1", params=["text"])
40 * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
41 */
42PyObject *
43PLy_spi_prepare(PyObject *self, PyObject *args)
44{
45 PLyPlanObject *plan;
46 PyObject *list = NULL((void*)0);
47 PyObject *volatile optr = NULL((void*)0);
48 char *query;
49 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
50 volatile MemoryContext oldcontext;
51 volatile ResourceOwner oldowner;
52 volatile int nargs;
53
54 if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
55 return NULL((void*)0);
56
57 if (list && (!PySequence_Check(list)))
58 {
59 PLy_exception_set(PyExc_TypeError,
60 "second argument of plpy.prepare must be a sequence");
61 return NULL((void*)0);
62 }
63
64 if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL((void*)0))
65 return NULL((void*)0);
66
67 plan->mcxt = AllocSetContextCreate(TopMemoryContext,
68 "PL/Python plan context",
69 ALLOCSET_DEFAULT_SIZES0, (8 * 1024), (8 * 1024 * 1024));
70 oldcontext = MemoryContextSwitchTo(plan->mcxt);
71
72 nargs = list ? PySequence_LengthPySequence_Size(list) : 0;
73
74 plan->nargs = nargs;
75 plan->types = nargs ? palloc0(sizeof(Oid) * nargs) : NULL((void*)0);
76 plan->values = nargs ? palloc0(sizeof(Datum) * nargs) : NULL((void*)0);
77 plan->args = nargs ? palloc0(sizeof(PLyObToDatum) * nargs) : NULL((void*)0);
78
79 MemoryContextSwitchTo(oldcontext);
80
81 oldcontext = CurrentMemoryContext;
82 oldowner = CurrentResourceOwner;
83
84 PLy_spi_subtransaction_begin(oldcontext, oldowner);
85
86 PG_TRY()do { sigjmp_buf *save_exception_stack = PG_exception_stack; ErrorContextCallback
*save_context_stack = error_context_stack; sigjmp_buf local_sigjmp_buf
; if (__sigsetjmp (local_sigjmp_buf, 0) == 0) { PG_exception_stack
= &local_sigjmp_buf
;
87 {
88 int i;
89
90 for (i = 0; i < nargs; i++)
91 {
92 char *sptr;
93 Oid typeId;
94 int32 typmod;
95
96 optr = PySequence_GetItem(list, i);
97 if (PyString_Check(optr)((((((PyObject*)(optr))->ob_type))->tp_flags & ((1L
<<27))) != 0)
)
98 sptr = PyString_AsString(optr);
99 else if (PyUnicode_Check(optr)((((((PyObject*)(optr))->ob_type))->tp_flags & ((1L
<<28))) != 0)
)
100 sptr = PLyUnicode_AsString(optr);
101 else
102 {
103 ereport(ERROR,do { if (errstart(20, "plpy_spi.c", 104, __func__, ("plpython"
"-" "11"))) errfinish (errmsg("plpy.prepare: type name at ordinal position %d is not a string"
, i)); if (__builtin_constant_p(20) && (20) >= 20)
abort(); } while(0)
104 (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)))do { if (errstart(20, "plpy_spi.c", 104, __func__, ("plpython"
"-" "11"))) errfinish (errmsg("plpy.prepare: type name at ordinal position %d is not a string"
, i)); if (__builtin_constant_p(20) && (20) >= 20)
abort(); } while(0)
;
105 sptr = NULL((void*)0); /* keep compiler quiet */
106 }
107
108 /********************************************************
109 * Resolve argument type names and then look them up by
110 * oid in the system cache, and remember the required
111 *information for input conversion.
112 ********************************************************/
113
114 parseTypeString(sptr, &typeId, &typmod, false((bool) 0));
115
116 Py_DECREF(optr)do { if ( --((PyObject*)(optr))->ob_refcnt != 0) ; else ( (
*(((PyObject*)((PyObject *)(optr)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(optr)))); } while (0)
;
117
118 /*
119 * set optr to NULL, so we won't try to unref it again in case of
120 * an error
121 */
122 optr = NULL((void*)0);
123
124 plan->types[i] = typeId;
125 PLy_output_setup_func(&plan->args[i], plan->mcxt,
126 typeId, typmod,
127 exec_ctx->curr_proc);
128 }
129
130 pg_verifymbstr(query, strlen(query), false((bool) 0));
131 plan->plan = SPI_prepare(query, plan->nargs, plan->types);
132 if (plan->plan == NULL((void*)0))
133 elog(ERROR, "SPI_prepare failed: %s",do { elog_start("plpy_spi.c", 134, __func__); elog_finish(20,
"SPI_prepare failed: %s", SPI_result_code_string(SPI_result)
); if (__builtin_constant_p(20) && (20) >= 20) abort
(); } while(0)
134 SPI_result_code_string(SPI_result))do { elog_start("plpy_spi.c", 134, __func__); elog_finish(20,
"SPI_prepare failed: %s", SPI_result_code_string(SPI_result)
); if (__builtin_constant_p(20) && (20) >= 20) abort
(); } while(0)
;
135
136 /* transfer plan from procCxt to topCxt */
137 if (SPI_keepplan(plan->plan))
138 elog(ERROR, "SPI_keepplan failed")do { elog_start("plpy_spi.c", 138, __func__); elog_finish(20,
"SPI_keepplan failed"); if (__builtin_constant_p(20) &&
(20) >= 20) abort(); } while(0)
;
139
140 PLy_spi_subtransaction_commit(oldcontext, oldowner);
141 }
142 PG_CATCH()} else { PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack
;
143 {
144 Py_DECREF(plan)do { if ( --((PyObject*)(plan))->ob_refcnt != 0) ; else ( (
*(((PyObject*)((PyObject *)(plan)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(plan)))); } while (0)
;
145 Py_XDECREF(optr)do { if ((optr) == ((void*)0)) ; else do { if ( --((PyObject*
)(optr))->ob_refcnt != 0) ; else ( (*(((PyObject*)((PyObject
*)(optr)))->ob_type)->tp_dealloc)((PyObject *)((PyObject
*)(optr)))); } while (0); } while (0)
;
146
147 PLy_spi_subtransaction_abort(oldcontext, oldowner);
148 return NULL((void*)0);
149 }
150 PG_END_TRY()} PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack; } while (0)
;
151
152 Assert(plan->plan != NULL)do { if (!(plan->plan != ((void*)0))) ExceptionalCondition
("!(plan->plan != ((void*)0))", ("FailedAssertion"), "plpy_spi.c"
, 152); } while (0)
;
153 return (PyObject *) plan;
154}
155
156/* execute(query="select * from foo", limit=5)
157 * execute(plan=plan, values=(foo, bar), limit=5)
158 */
159PyObject *
160PLy_spi_execute(PyObject *self, PyObject *args)
161{
162 char *query;
163 PyObject *plan;
164 PyObject *list = NULL((void*)0);
165 long limit = 0;
166
167 if (PyArg_ParseTuple(args, "s|l", &query, &limit))
168 return PLy_spi_execute_query(query, limit);
169
170 PyErr_Clear();
171
172 if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
173 is_PLyPlanObject(plan))
174 return PLy_spi_execute_plan(plan, list, limit);
175
176 PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
177 return NULL((void*)0);
178}
179
180PyObject *
181PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
182{
183 volatile int nargs;
184 int i,
185 rv;
186 PLyPlanObject *plan;
187 volatile MemoryContext oldcontext;
188 volatile ResourceOwner oldowner;
189 PyObject *ret;
190
191 if (list != NULL((void*)0))
192 {
193 if (!PySequence_Check(list) || PyString_Check(list)((((((PyObject*)(list))->ob_type))->tp_flags & ((1L
<<27))) != 0)
|| PyUnicode_Check(list)((((((PyObject*)(list))->ob_type))->tp_flags & ((1L
<<28))) != 0)
)
194 {
195 PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
196 return NULL((void*)0);
197 }
198 nargs = PySequence_LengthPySequence_Size(list);
199 }
200 else
201 nargs = 0;
202
203 plan = (PLyPlanObject *) ob;
204
205 if (nargs != plan->nargs)
206 {
207 char *sv;
208 PyObject *so = PyObject_Str(list);
209
210 if (!so)
211 PLy_elog(ERROR, "could not execute plan")do { PLy_elog_impl(20, "could not execute plan"); if (__builtin_constant_p
(20) && (20) >= 20) abort(); } while(0)
;
212 sv = PyString_AsString(so);
213 PLy_exception_set_plural(PyExc_TypeError,
214 "Expected sequence of %d argument, got %d: %s",
215 "Expected sequence of %d arguments, got %d: %s",
216 plan->nargs,
217 plan->nargs, nargs, sv);
218 Py_DECREF(so)do { if ( --((PyObject*)(so))->ob_refcnt != 0) ; else ( (*
(((PyObject*)((PyObject *)(so)))->ob_type)->tp_dealloc)
((PyObject *)((PyObject *)(so)))); } while (0)
;
219
220 return NULL((void*)0);
221 }
222
223 oldcontext = CurrentMemoryContext;
224 oldowner = CurrentResourceOwner;
225
226 PLy_spi_subtransaction_begin(oldcontext, oldowner);
227
228 PG_TRY()do { sigjmp_buf *save_exception_stack = PG_exception_stack; ErrorContextCallback
*save_context_stack = error_context_stack; sigjmp_buf local_sigjmp_buf
; if (__sigsetjmp (local_sigjmp_buf, 0) == 0) { PG_exception_stack
= &local_sigjmp_buf
;
229 {
230 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
231 char *volatile nulls;
232 volatile int j;
233
234 if (nargs > 0)
235 nulls = palloc(nargs * sizeof(char));
236 else
237 nulls = NULL((void*)0);
238
239 for (j = 0; j < nargs; j++)
240 {
241 PLyObToDatum *arg = &plan->args[j];
242 PyObject *elem;
243
244 elem = PySequence_GetItem(list, j);
245 PG_TRY()do { sigjmp_buf *save_exception_stack = PG_exception_stack; ErrorContextCallback
*save_context_stack = error_context_stack; sigjmp_buf local_sigjmp_buf
; if (__sigsetjmp (local_sigjmp_buf, 0) == 0) { PG_exception_stack
= &local_sigjmp_buf
;
246 {
247 bool isnull;
248
249 plan->values[j] = PLy_output_convert(arg, elem, &isnull);
250 nulls[j] = isnull ? 'n' : ' ';
251 }
252 PG_CATCH()} else { PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack
;
253 {
254 Py_DECREF(elem)do { if ( --((PyObject*)(elem))->ob_refcnt != 0) ; else ( (
*(((PyObject*)((PyObject *)(elem)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(elem)))); } while (0)
;
255 PG_RE_THROW()pg_re_throw();
256 }
257 PG_END_TRY()} PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack; } while (0)
;
258 Py_DECREF(elem)do { if ( --((PyObject*)(elem))->ob_refcnt != 0) ; else ( (
*(((PyObject*)((PyObject *)(elem)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(elem)))); } while (0)
;
259 }
260
261 rv = SPI_execute_plan(plan->plan, plan->values, nulls,
262 exec_ctx->curr_proc->fn_readonly, limit);
263 ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
264
265 if (nargs > 0)
266 pfree(nulls);
267
268 PLy_spi_subtransaction_commit(oldcontext, oldowner);
269 }
270 PG_CATCH()} else { PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack
;
271 {
272 int k;
273
274 /*
275 * cleanup plan->values array
276 */
277 for (k = 0; k < nargs; k++)
278 {
279 if (!plan->args[k].typbyval &&
280 (plan->values[k] != PointerGetDatum(NULL)((Datum) (((void*)0)))))
281 {
282 pfree(DatumGetPointer(plan->values[k])((Pointer) (plan->values[k])));
283 plan->values[k] = PointerGetDatum(NULL)((Datum) (((void*)0)));
284 }
285 }
286
287 PLy_spi_subtransaction_abort(oldcontext, oldowner);
288 return NULL((void*)0);
289 }
290 PG_END_TRY()} PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack; } while (0)
;
291
292 for (i = 0; i < nargs; i++)
293 {
294 if (!plan->args[i].typbyval &&
295 (plan->values[i] != PointerGetDatum(NULL)((Datum) (((void*)0)))))
296 {
297 pfree(DatumGetPointer(plan->values[i])((Pointer) (plan->values[i])));
298 plan->values[i] = PointerGetDatum(NULL)((Datum) (((void*)0)));
299 }
300 }
301
302 if (rv < 0)
303 {
304 PLy_exception_set(PLy_exc_spi_error,
305 "SPI_execute_plan failed: %s",
306 SPI_result_code_string(rv));
307 return NULL((void*)0);
308 }
309
310 return ret;
311}
312
313static PyObject *
314PLy_spi_execute_query(char *query, long limit)
315{
316 int rv;
317 volatile MemoryContext oldcontext;
318 volatile ResourceOwner oldowner;
319 PyObject *ret = NULL((void*)0);
320
321 oldcontext = CurrentMemoryContext;
322 oldowner = CurrentResourceOwner;
323
324 PLy_spi_subtransaction_begin(oldcontext, oldowner);
325
326 PG_TRY()do { sigjmp_buf *save_exception_stack = PG_exception_stack; ErrorContextCallback
*save_context_stack = error_context_stack; sigjmp_buf local_sigjmp_buf
; if (__sigsetjmp (local_sigjmp_buf, 0) == 0) { PG_exception_stack
= &local_sigjmp_buf
;
327 {
328 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
329
330 pg_verifymbstr(query, strlen(query), false((bool) 0));
331 rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
332 ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
333
334 PLy_spi_subtransaction_commit(oldcontext, oldowner);
335 }
336 PG_CATCH()} else { PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack
;
337 {
338 PLy_spi_subtransaction_abort(oldcontext, oldowner);
339 return NULL((void*)0);
340 }
341 PG_END_TRY()} PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack; } while (0)
;
342
343 if (rv < 0)
344 {
345 Py_XDECREF(ret)do { if ((ret) == ((void*)0)) ; else do { if ( --((PyObject*)
(ret))->ob_refcnt != 0) ; else ( (*(((PyObject*)((PyObject
*)(ret)))->ob_type)->tp_dealloc)((PyObject *)((PyObject
*)(ret)))); } while (0); } while (0)
;
346 PLy_exception_set(PLy_exc_spi_error,
347 "SPI_execute failed: %s",
348 SPI_result_code_string(rv));
349 return NULL((void*)0);
350 }
351
352 return ret;
353}
354
355static PyObject *
356PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
357{
358 PLyResultObject *result;
359 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
360 volatile MemoryContext oldcontext;
361
362 result = (PLyResultObject *) PLy_result_new();
363 if (!result)
1
Assuming 'result' is non-null
2
Taking false branch
364 return NULL((void*)0);
365 Py_DECREF(result->status)do { if ( --((PyObject*)(result->status))->ob_refcnt !=
0) ; else ( (*(((PyObject*)((PyObject *)(result->status))
)->ob_type)->tp_dealloc)((PyObject *)((PyObject *)(result
->status)))); } while (0)
;
366 result->status = PyInt_FromLong(status);
367
368 if (status > 0 && tuptable == NULL((void*)0))
3
Assuming 'status' is > 0
4
Assuming 'tuptable' is not equal to null
5
Taking false branch
369 {
370 Py_DECREF(result->nrows)do { if ( --((PyObject*)(result->nrows))->ob_refcnt != 0
) ; else ( (*(((PyObject*)((PyObject *)(result->nrows)))->
ob_type)->tp_dealloc)((PyObject *)((PyObject *)(result->
nrows)))); } while (0)
;
371 result->nrows = (rows > (uint64) LONG_MAX9223372036854775807L) ?
372 PyFloat_FromDouble((double) rows) :
373 PyInt_FromLong((long) rows);
374 }
375 else if (status > 0 && tuptable != NULL((void*)0))
6
Taking true branch
376 {
377 PLyDatumToOb ininfo;
378 MemoryContext cxt;
379
380 Py_DECREF(result->nrows)do { if ( --((PyObject*)(result->nrows))->ob_refcnt != 0
) ; else ( (*(((PyObject*)((PyObject *)(result->nrows)))->
ob_type)->tp_dealloc)((PyObject *)((PyObject *)(result->
nrows)))); } while (0)
;
381 result->nrows = (rows > (uint64) LONG_MAX9223372036854775807L) ?
7
Assuming 'rows' is <= 9223372036854775807
8
'?' condition is false
382 PyFloat_FromDouble((double) rows) :
383 PyInt_FromLong((long) rows);
384
385 cxt = AllocSetContextCreate(CurrentMemoryContext,
386 "PL/Python temp context",
387 ALLOCSET_DEFAULT_SIZES0, (8 * 1024), (8 * 1024 * 1024));
388
389 /* Initialize for converting result tuples to Python */
390 PLy_input_setup_func(&ininfo, cxt, RECORDOID2249, -1,
391 exec_ctx->curr_proc);
392
393 oldcontext = CurrentMemoryContext;
394 PG_TRY()do { sigjmp_buf *save_exception_stack = PG_exception_stack; ErrorContextCallback
*save_context_stack = error_context_stack; sigjmp_buf local_sigjmp_buf
; if (__sigsetjmp (local_sigjmp_buf, 0) == 0) { PG_exception_stack
= &local_sigjmp_buf
;
395 {
396 MemoryContext oldcontext2;
397
398 if (rows)
9
Assuming 'rows' is not equal to 0
10
Taking true branch
399 {
400 uint64 i;
401
402 /*
403 * PyList_New() and PyList_SetItem() use Py_ssize_t for list
404 * size and list indices; so we cannot support a result larger
405 * than PY_SSIZE_T_MAX.
406 */
407 if (rows > (uint64) PY_SSIZE_T_MAX((Py_ssize_t)(((size_t)-1)>>1)))
11
Taking false branch
408 ereport(ERROR,do { if (errstart(20, "plpy_spi.c", 410, __func__, ("plpython"
"-" "11"))) errfinish (errcode((((('5') - '0') & 0x3F) +
(((('4') - '0') & 0x3F) << 6) + (((('0') - '0') &
0x3F) << 12) + (((('0') - '0') & 0x3F) << 18
) + (((('0') - '0') & 0x3F) << 24))), errmsg("query result has too many rows to fit in a Python list"
)); if (__builtin_constant_p(20) && (20) >= 20) abort
(); } while(0)
409 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),do { if (errstart(20, "plpy_spi.c", 410, __func__, ("plpython"
"-" "11"))) errfinish (errcode((((('5') - '0') & 0x3F) +
(((('4') - '0') & 0x3F) << 6) + (((('0') - '0') &
0x3F) << 12) + (((('0') - '0') & 0x3F) << 18
) + (((('0') - '0') & 0x3F) << 24))), errmsg("query result has too many rows to fit in a Python list"
)); if (__builtin_constant_p(20) && (20) >= 20) abort
(); } while(0)
410 errmsg("query result has too many rows to fit in a Python list")))do { if (errstart(20, "plpy_spi.c", 410, __func__, ("plpython"
"-" "11"))) errfinish (errcode((((('5') - '0') & 0x3F) +
(((('4') - '0') & 0x3F) << 6) + (((('0') - '0') &
0x3F) << 12) + (((('0') - '0') & 0x3F) << 18
) + (((('0') - '0') & 0x3F) << 24))), errmsg("query result has too many rows to fit in a Python list"
)); if (__builtin_constant_p(20) && (20) >= 20) abort
(); } while(0)
;
411
412 Py_DECREF(result->rows)do { if ( --((PyObject*)(result->rows))->ob_refcnt != 0
) ; else ( (*(((PyObject*)((PyObject *)(result->rows)))->
ob_type)->tp_dealloc)((PyObject *)((PyObject *)(result->
rows)))); } while (0)
;
413 result->rows = PyList_New(rows);
414 if (!result->rows)
12
Taking true branch
415 {
416 Py_DECREF(result)do { if ( --((PyObject*)(result))->ob_refcnt != 0) ; else (
(*(((PyObject*)((PyObject *)(result)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(result)))); } while (0)
;
417 result = NULL((void*)0);
13
Null pointer value stored to 'result'
418 }
419 else
420 {
421 PLy_input_setup_tuple(&ininfo, tuptable->tupdesc,
422 exec_ctx->curr_proc);
423
424 for (i = 0; i < rows; i++)
425 {
426 PyObject *row = PLy_input_from_tuple(&ininfo,
427 tuptable->vals[i],
428 tuptable->tupdesc);
429
430 PyList_SetItem(result->rows, i, row);
431 }
432 }
433 }
434
435 /*
436 * Save tuple descriptor for later use by result set metadata
437 * functions. Save it in TopMemoryContext so that it survives
438 * outside of an SPI context. We trust that PLy_result_dealloc()
439 * will clean it up when the time is right. (Do this as late as
440 * possible, to minimize the number of ways the tupdesc could get
441 * leaked due to errors.)
442 */
443 oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
444 result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
14
Access to field 'tupdesc' results in a dereference of a null pointer (loaded from variable 'result')
445 MemoryContextSwitchTo(oldcontext2);
446 }
447 PG_CATCH()} else { PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack
;
448 {
449 MemoryContextSwitchTo(oldcontext);
450 MemoryContextDelete(cxt);
451 Py_DECREF(result)do { if ( --((PyObject*)(result))->ob_refcnt != 0) ; else (
(*(((PyObject*)((PyObject *)(result)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(result)))); } while (0)
;
452 PG_RE_THROW()pg_re_throw();
453 }
454 PG_END_TRY()} PG_exception_stack = save_exception_stack; error_context_stack
= save_context_stack; } while (0)
;
455
456 MemoryContextDelete(cxt);
457 SPI_freetuptable(tuptable);
458 }
459
460 return (PyObject *) result;
461}
462
463/*
464 * Utilities for running SPI functions in subtransactions.
465 *
466 * Usage:
467 *
468 * MemoryContext oldcontext = CurrentMemoryContext;
469 * ResourceOwner oldowner = CurrentResourceOwner;
470 *
471 * PLy_spi_subtransaction_begin(oldcontext, oldowner);
472 * PG_TRY();
473 * {
474 * <call SPI functions>
475 * PLy_spi_subtransaction_commit(oldcontext, oldowner);
476 * }
477 * PG_CATCH();
478 * {
479 * <do cleanup>
480 * PLy_spi_subtransaction_abort(oldcontext, oldowner);
481 * return NULL;
482 * }
483 * PG_END_TRY();
484 *
485 * These utilities take care of restoring connection to the SPI manager and
486 * setting a Python exception in case of an abort.
487 */
488void
489PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
490{
491 BeginInternalSubTransaction(NULL((void*)0));
492 /* Want to run inside function's memory context */
493 MemoryContextSwitchTo(oldcontext);
494}
495
496void
497PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
498{
499 /* Commit the inner transaction, return to outer xact context */
500 ReleaseCurrentSubTransaction();
501 MemoryContextSwitchTo(oldcontext);
502 CurrentResourceOwner = oldowner;
503}
504
505void
506PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
507{
508 ErrorData *edata;
509 PLyExceptionEntry *entry;
510 PyObject *exc;
511
512 /* Save error info */
513 MemoryContextSwitchTo(oldcontext);
514 edata = CopyErrorData();
515 FlushErrorState();
516
517 /* Abort the inner transaction */
518 RollbackAndReleaseCurrentSubTransaction();
519 MemoryContextSwitchTo(oldcontext);
520 CurrentResourceOwner = oldowner;
521
522 /* Look up the correct exception */
523 entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
524 HASH_FIND, NULL((void*)0));
525
526 /*
527 * This could be a custom error code, if that's the case fallback to
528 * SPIError
529 */
530 exc = entry ? entry->exc : PLy_exc_spi_error;
531 /* Make Python raise the exception */
532 PLy_spi_exception_set(exc, edata);
533 FreeErrorData(edata);
534}
535
536/*
537 * Raise a SPIError, passing in it more error details, like the
538 * internal query and error position.
539 */
540static void
541PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
542{
543 PyObject *args = NULL((void*)0);
544 PyObject *spierror = NULL((void*)0);
545 PyObject *spidata = NULL((void*)0);
546
547 args = Py_BuildValue("(s)", edata->message);
548 if (!args)
549 goto failure;
550
551 /* create a new SPI exception with the error message as the parameter */
552 spierror = PyObject_CallObject(excclass, args);
553 if (!spierror)
554 goto failure;
555
556 spidata = Py_BuildValue("(izzzizzzzz)", edata->sqlerrcode, edata->detail, edata->hint,
557 edata->internalquery, edata->internalpos,
558 edata->schema_name, edata->table_name, edata->column_name,
559 edata->datatype_name, edata->constraint_name);
560 if (!spidata)
561 goto failure;
562
563 if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
564 goto failure;
565
566 PyErr_SetObject(excclass, spierror);
567
568 Py_DECREF(args)do { if ( --((PyObject*)(args))->ob_refcnt != 0) ; else ( (
*(((PyObject*)((PyObject *)(args)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(args)))); } while (0)
;
569 Py_DECREF(spierror)do { if ( --((PyObject*)(spierror))->ob_refcnt != 0) ; else
( (*(((PyObject*)((PyObject *)(spierror)))->ob_type)->
tp_dealloc)((PyObject *)((PyObject *)(spierror)))); } while (
0)
;
570 Py_DECREF(spidata)do { if ( --((PyObject*)(spidata))->ob_refcnt != 0) ; else
( (*(((PyObject*)((PyObject *)(spidata)))->ob_type)->tp_dealloc
)((PyObject *)((PyObject *)(spidata)))); } while (0)
;
571 return;
572
573failure:
574 Py_XDECREF(args)do { if ((args) == ((void*)0)) ; else do { if ( --((PyObject*
)(args))->ob_refcnt != 0) ; else ( (*(((PyObject*)((PyObject
*)(args)))->ob_type)->tp_dealloc)((PyObject *)((PyObject
*)(args)))); } while (0); } while (0)
;
575 Py_XDECREF(spierror)do { if ((spierror) == ((void*)0)) ; else do { if ( --((PyObject
*)(spierror))->ob_refcnt != 0) ; else ( (*(((PyObject*)((PyObject
*)(spierror)))->ob_type)->tp_dealloc)((PyObject *)((PyObject
*)(spierror)))); } while (0); } while (0)
;
576 Py_XDECREF(spidata)do { if ((spidata) == ((void*)0)) ; else do { if ( --((PyObject
*)(spidata))->ob_refcnt != 0) ; else ( (*(((PyObject*)((PyObject
*)(spidata)))->ob_type)->tp_dealloc)((PyObject *)((PyObject
*)(spidata)))); } while (0); } while (0)
;
577 elog(ERROR, "could not convert SPI error to Python exception")do { elog_start("plpy_spi.c", 577, __func__); elog_finish(20,
"could not convert SPI error to Python exception"); if (__builtin_constant_p
(20) && (20) >= 20) abort(); } while(0)
;
578}