Peter, I built plpython with scan-build using Python 2.7.12 and Clang 3.8. On master, I got 13 warnings, and with your patches only one warning (report attached).
Make installcheck passes. Let me know if I can test anything else. -John NaylorTitle: plpy_spi.c
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 | ||||
32 | static PyObject *PLy_spi_execute_query(char *query, long limit); | |||
33 | static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable, | |||
34 | uint64 rows, int status); | |||
35 | static 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 | */ | |||
42 | PyObject * | |||
43 | PLy_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 | */ | |||
159 | PyObject * | |||
160 | PLy_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 | ||||
180 | PyObject * | |||
181 | PLy_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 | ||||
313 | static PyObject * | |||
314 | PLy_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 | ||||
355 | static PyObject * | |||
356 | PLy_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) | |||
| ||||
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)) | |||
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)) | |||
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) ? | |||
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) | |||
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))) | |||
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) | |||
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); | |||
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); | |||
| ||||
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 | */ | |||
488 | void | |||
489 | PLy_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 | ||||
496 | void | |||
497 | PLy_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 | ||||
505 | void | |||
506 | PLy_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 | */ | |||
540 | static void | |||
541 | PLy_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 | ||||
573 | failure: | |||
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 | } |