Age Owner Branch data 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 *
4501 peter_e@gmx.net 39 :CBC 26 : PLy_spi_prepare(PyObject *self, PyObject *args)
40 : : {
41 : : PLyPlanObject *plan;
42 : 26 : PyObject *list = NULL;
43 : 26 : PyObject *volatile optr = NULL;
44 : : char *query;
2341 tgl@sss.pgh.pa.us 45 : 26 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
46 : : volatile MemoryContext oldcontext;
47 : : volatile ResourceOwner oldowner;
48 : : volatile int nargs;
49 : :
2726 peter_e@gmx.net 50 [ - + ]: 26 : if (!PyArg_ParseTuple(args, "s|O:prepare", &query, &list))
4501 peter_e@gmx.net 51 :UBC 0 : return NULL;
52 : :
4501 peter_e@gmx.net 53 [ + + - + ]:CBC 26 : if (list && (!PySequence_Check(list)))
54 : : {
4501 peter_e@gmx.net 55 :UBC 0 : PLy_exception_set(PyExc_TypeError,
56 : : "second argument of plpy.prepare must be a sequence");
57 : 0 : return NULL;
58 : : }
59 : :
4501 peter_e@gmx.net 60 [ - + ]:CBC 26 : if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
4501 peter_e@gmx.net 61 :UBC 0 : return NULL;
62 : :
3083 tgl@sss.pgh.pa.us 63 :CBC 26 : plan->mcxt = AllocSetContextCreate(TopMemoryContext,
64 : : "PL/Python plan context",
65 : : ALLOCSET_DEFAULT_SIZES);
66 : 26 : oldcontext = MemoryContextSwitchTo(plan->mcxt);
67 : :
4501 peter_e@gmx.net 68 [ + + ]: 26 : nargs = list ? PySequence_Length(list) : 0;
69 : :
70 : 26 : plan->nargs = nargs;
2341 tgl@sss.pgh.pa.us 71 [ + + ]: 26 : plan->types = nargs ? palloc0(sizeof(Oid) * nargs) : NULL;
72 [ + + ]: 26 : plan->values = nargs ? palloc0(sizeof(Datum) * nargs) : NULL;
73 [ + + ]: 26 : plan->args = nargs ? palloc0(sizeof(PLyObToDatum) * nargs) : NULL;
74 : :
3083 75 : 26 : MemoryContextSwitchTo(oldcontext);
76 : :
4501 peter_e@gmx.net 77 : 26 : oldcontext = CurrentMemoryContext;
78 : 26 : oldowner = CurrentResourceOwner;
79 : :
80 : 26 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
81 : :
82 [ + + ]: 26 : PG_TRY();
83 : : {
84 : : int i;
85 : :
86 [ + + ]: 40 : for (i = 0; i < nargs; i++)
87 : : {
88 : : char *sptr;
89 : : Oid typeId;
90 : : int32 typmod;
91 : :
92 : 17 : optr = PySequence_GetItem(list, i);
769 andres@anarazel.de 93 [ + - ]: 17 : if (PyUnicode_Check(optr))
4501 peter_e@gmx.net 94 : 17 : sptr = PLyUnicode_AsString(optr);
95 : : else
96 : : {
4501 peter_e@gmx.net 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 : :
474 tgl@sss.pgh.pa.us 108 :CBC 17 : (void) parseTypeString(sptr, &typeId, &typmod, NULL);
109 : :
4501 peter_e@gmx.net 110 : 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;
2341 tgl@sss.pgh.pa.us 119 : 14 : PLy_output_setup_func(&plan->args[i], plan->mcxt,
120 : : typeId, typmod,
121 : 14 : exec_ctx->curr_proc);
122 : : }
123 : :
4501 peter_e@gmx.net 124 : 23 : pg_verifymbstr(query, strlen(query), false);
125 : 23 : plan->plan = SPI_prepare(query, plan->nargs, plan->types);
126 [ - + ]: 23 : if (plan->plan == NULL)
4501 peter_e@gmx.net 127 [ # # ]:UBC 0 : elog(ERROR, "SPI_prepare failed: %s",
128 : : SPI_result_code_string(SPI_result));
129 : :
130 : : /* transfer plan from procCxt to topCxt */
4501 peter_e@gmx.net 131 [ - + ]:CBC 23 : if (SPI_keepplan(plan->plan))
4501 peter_e@gmx.net 132 [ # # ]:UBC 0 : elog(ERROR, "SPI_keepplan failed");
133 : :
4501 peter_e@gmx.net 134 :CBC 23 : 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 [ - + ]: 23 : PG_END_TRY();
145 : :
146 [ - + ]: 23 : Assert(plan->plan != NULL);
147 : 23 : 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 : :
4501 peter_e@gmx.net 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 *
4501 peter_e@gmx.net 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 : : {
769 andres@anarazel.de 187 [ + - - + ]: 16 : if (!PySequence_Check(list) || PyUnicode_Check(list))
188 : : {
4501 peter_e@gmx.net 189 :UBC 0 : PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
190 : 0 : return NULL;
191 : : }
4501 peter_e@gmx.net 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;
4501 peter_e@gmx.net 202 :UBC 0 : PyObject *so = PyObject_Str(list);
203 : :
204 [ # # ]: 0 : if (!so)
205 : 0 : PLy_elog(ERROR, "could not execute plan");
769 andres@anarazel.de 206 : 0 : sv = PLyUnicode_AsString(so);
4501 peter_e@gmx.net 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 : :
4501 peter_e@gmx.net 217 :CBC 22 : oldcontext = CurrentMemoryContext;
218 : 22 : oldowner = CurrentResourceOwner;
219 : :
220 : 22 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
221 : :
222 [ + + ]: 22 : PG_TRY();
223 : : {
4415 tgl@sss.pgh.pa.us 224 : 22 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
225 : : char *volatile nulls;
226 : : volatile int j;
227 : :
4501 peter_e@gmx.net 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 : : {
2341 tgl@sss.pgh.pa.us 235 : 16 : PLyObToDatum *arg = &plan->args[j];
236 : : PyObject *elem;
237 : :
4501 peter_e@gmx.net 238 : 16 : elem = PySequence_GetItem(list, j);
555 drowley@postgresql.o 239 [ + + ]: 16 : PG_TRY(2);
240 : : {
241 : : bool isnull;
242 : :
2341 tgl@sss.pgh.pa.us 243 : 16 : plan->values[j] = PLy_output_convert(arg, elem, &isnull);
244 [ - + ]: 14 : nulls[j] = isnull ? 'n' : ' ';
245 : : }
555 drowley@postgresql.o 246 : 2 : PG_FINALLY(2);
247 : : {
4501 peter_e@gmx.net 248 : 16 : Py_DECREF(elem);
249 : : }
555 drowley@postgresql.o 250 [ + + ]: 16 : PG_END_TRY(2);
251 : : }
252 : :
4501 peter_e@gmx.net 253 : 40 : rv = SPI_execute_plan(plan->plan, plan->values, nulls,
4415 tgl@sss.pgh.pa.us 254 : 20 : exec_ctx->curr_proc->fn_readonly, limit);
4501 peter_e@gmx.net 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 : : {
2341 tgl@sss.pgh.pa.us 271 [ - + ]: 2 : if (!plan->args[k].typbyval &&
4501 peter_e@gmx.net 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 : :
4501 peter_e@gmx.net 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 : : {
2341 tgl@sss.pgh.pa.us 286 [ + + ]: 14 : if (!plan->args[i].typbyval &&
4501 peter_e@gmx.net 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;
4415 tgl@sss.pgh.pa.us 311 : 141 : PyObject *ret = NULL;
312 : :
4501 peter_e@gmx.net 313 : 141 : oldcontext = CurrentMemoryContext;
314 : 141 : oldowner = CurrentResourceOwner;
315 : :
316 : 141 : PLy_spi_subtransaction_begin(oldcontext, oldowner);
317 : :
318 [ + + ]: 141 : PG_TRY();
319 : : {
4326 bruce@momjian.us 320 : 141 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
321 : :
4501 peter_e@gmx.net 322 : 141 : pg_verifymbstr(query, strlen(query), false);
4415 tgl@sss.pgh.pa.us 323 : 141 : rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
4501 peter_e@gmx.net 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 : : {
4415 tgl@sss.pgh.pa.us 337 : 1 : Py_XDECREF(ret);
4501 peter_e@gmx.net 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 *
2955 tgl@sss.pgh.pa.us 348 : 140 : PLy_spi_execute_fetch_result(SPITupleTable *tuptable, uint64 rows, int status)
349 : : {
350 : : PLyResultObject *result;
2341 351 : 140 : PLyExecutionContext *exec_ctx = PLy_current_execution_context();
352 : : volatile MemoryContext oldcontext;
353 : :
4501 peter_e@gmx.net 354 : 140 : result = (PLyResultObject *) PLy_result_new();
2357 355 [ - + ]: 140 : if (!result)
356 : : {
2322 peter_e@gmx.net 357 :UBC 0 : SPI_freetuptable(tuptable);
2357 358 : 0 : return NULL;
359 : : }
4501 peter_e@gmx.net 360 :CBC 140 : Py_DECREF(result->status);
769 andres@anarazel.de 361 : 140 : result->status = PyLong_FromLong(status);
362 : :
4501 peter_e@gmx.net 363 [ + + + + ]: 140 : if (status > 0 && tuptable == NULL)
364 : : {
365 : 79 : Py_DECREF(result->nrows);
2276 366 : 79 : result->nrows = PyLong_FromUnsignedLongLong(rows);
367 : : }
4501 368 [ + + + - ]: 61 : else if (status > 0 && tuptable != NULL)
369 : : {
370 : : PLyDatumToOb ininfo;
371 : : MemoryContext cxt;
372 : :
373 : 59 : Py_DECREF(result->nrows);
2276 374 : 59 : result->nrows = PyLong_FromUnsignedLongLong(rows);
375 : :
3083 tgl@sss.pgh.pa.us 376 : 59 : cxt = AllocSetContextCreate(CurrentMemoryContext,
377 : : "PL/Python temp context",
378 : : ALLOCSET_DEFAULT_SIZES);
379 : :
380 : : /* Initialize for converting result tuples to Python */
2341 381 : 59 : PLy_input_setup_func(&ininfo, cxt, RECORDOID, -1,
382 : 59 : exec_ctx->curr_proc);
383 : :
4501 peter_e@gmx.net 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 : : */
2955 tgl@sss.pgh.pa.us 398 [ - + ]: 57 : if (rows > (uint64) PY_SSIZE_T_MAX)
2955 tgl@sss.pgh.pa.us 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 : :
4501 peter_e@gmx.net 403 :CBC 57 : Py_DECREF(result->rows);
404 : 57 : result->rows = PyList_New(rows);
2322 405 [ + - ]: 57 : if (result->rows)
406 : : {
2357 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 : : */
3921 tgl@sss.pgh.pa.us 430 : 59 : oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
431 : 59 : result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
432 : 59 : MemoryContextSwitchTo(oldcontext2);
433 : : }
4501 peter_e@gmx.net 434 :UBC 0 : PG_CATCH();
435 : : {
436 : 0 : MemoryContextSwitchTo(oldcontext);
3083 tgl@sss.pgh.pa.us 437 : 0 : MemoryContextDelete(cxt);
4501 peter_e@gmx.net 438 : 0 : Py_DECREF(result);
3921 tgl@sss.pgh.pa.us 439 : 0 : PG_RE_THROW();
440 : : }
4501 peter_e@gmx.net 441 [ - + ]:CBC 59 : PG_END_TRY();
442 : :
3083 tgl@sss.pgh.pa.us 443 : 59 : MemoryContextDelete(cxt);
4501 peter_e@gmx.net 444 : 59 : SPI_freetuptable(tuptable);
445 : :
446 : : /* in case PyList_New() failed above */
2322 447 [ - + ]: 59 : if (!result->rows)
448 : : {
2322 peter_e@gmx.net 449 :UBC 0 : Py_DECREF(result);
450 : 0 : result = NULL;
451 : : }
452 : : }
453 : :
4501 peter_e@gmx.net 454 :CBC 140 : return (PyObject *) result;
455 : : }
456 : :
457 : : PyObject *
776 tgl@sss.pgh.pa.us 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 : : }
776 tgl@sss.pgh.pa.us 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 : : }
776 tgl@sss.pgh.pa.us 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
4501 peter_e@gmx.net 577 : 252 : PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
578 : : {
579 : 252 : BeginInternalSubTransaction(NULL);
580 : : /* Want to run inside function's memory context */
581 : 252 : MemoryContextSwitchTo(oldcontext);
582 : 252 : }
583 : :
584 : : void
585 : 225 : PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
586 : : {
587 : : /* Commit the inner transaction, return to outer xact context */
588 : 225 : ReleaseCurrentSubTransaction();
589 : 225 : MemoryContextSwitchTo(oldcontext);
590 : 225 : CurrentResourceOwner = oldowner;
591 : 225 : }
592 : :
593 : : void
594 : 27 : PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
595 : : {
596 : : ErrorData *edata;
597 : : PLyExceptionEntry *entry;
598 : : PyObject *exc;
599 : :
600 : : /* Save error info */
601 : 27 : MemoryContextSwitchTo(oldcontext);
602 : 27 : edata = CopyErrorData();
603 : 27 : FlushErrorState();
604 : :
605 : : /* Abort the inner transaction */
606 : 27 : RollbackAndReleaseCurrentSubTransaction();
607 : 27 : MemoryContextSwitchTo(oldcontext);
608 : 27 : CurrentResourceOwner = oldowner;
609 : :
610 : : /* Look up the correct exception */
611 : 27 : 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 [ + + ]: 27 : exc = entry ? entry->exc : PLy_exc_spi_error;
619 : : /* Make Python raise the exception */
620 : 27 : PLy_spi_exception_set(exc, edata);
621 : 27 : FreeErrorData(edata);
622 : 27 : }
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 : 33 : PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
630 : : {
631 : 33 : PyObject *args = NULL;
632 : 33 : PyObject *spierror = NULL;
633 : 33 : PyObject *spidata = NULL;
634 : :
635 : 33 : args = Py_BuildValue("(s)", edata->message);
636 [ - + ]: 33 : if (!args)
4501 peter_e@gmx.net 637 :UBC 0 : goto failure;
638 : :
639 : : /* create a new SPI exception with the error message as the parameter */
4501 peter_e@gmx.net 640 :CBC 33 : spierror = PyObject_CallObject(excclass, args);
641 [ - + ]: 33 : if (!spierror)
4501 peter_e@gmx.net 642 :UBC 0 : goto failure;
643 : :
2866 rhaas@postgresql.org 644 :CBC 33 : 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);
4501 peter_e@gmx.net 648 [ - + ]: 33 : if (!spidata)
4501 peter_e@gmx.net 649 :UBC 0 : goto failure;
650 : :
4501 peter_e@gmx.net 651 [ - + ]:CBC 33 : if (PyObject_SetAttrString(spierror, "spidata", spidata) == -1)
4501 peter_e@gmx.net 652 :UBC 0 : goto failure;
653 : :
4501 peter_e@gmx.net 654 :CBC 33 : PyErr_SetObject(excclass, spierror);
655 : :
656 : 33 : Py_DECREF(args);
657 : 33 : Py_DECREF(spierror);
658 : 33 : Py_DECREF(spidata);
659 : 33 : return;
660 : :
4501 peter_e@gmx.net 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 : : }
|