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