~ubuntu-branches/debian/sid/postgresql-9.3/sid

« back to all changes in this revision

Viewing changes to src/pl/plpython/plpy_spi.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2013-05-08 05:39:52 UTC
  • Revision ID: package-import@ubuntu.com-20130508053952-1j7uilp7mjtrvq8q
Tags: upstream-9.3~beta1
ImportĀ upstreamĀ versionĀ 9.3~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * interface to SPI functions
 
3
 *
 
4
 * src/pl/plpython/plpy_spi.c
 
5
 */
 
6
 
 
7
#include "postgres.h"
 
8
 
 
9
#include "access/htup_details.h"
 
10
#include "access/xact.h"
 
11
#include "catalog/pg_type.h"
 
12
#include "executor/spi.h"
 
13
#include "mb/pg_wchar.h"
 
14
#include "parser/parse_type.h"
 
15
#include "utils/memutils.h"
 
16
#include "utils/syscache.h"
 
17
 
 
18
#include "plpython.h"
 
19
 
 
20
#include "plpy_spi.h"
 
21
 
 
22
#include "plpy_elog.h"
 
23
#include "plpy_main.h"
 
24
#include "plpy_planobject.h"
 
25
#include "plpy_plpymodule.h"
 
26
#include "plpy_procedure.h"
 
27
#include "plpy_resultobject.h"
 
28
 
 
29
 
 
30
static PyObject *PLy_spi_execute_query(char *query, long limit);
 
31
static PyObject *PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit);
 
32
static PyObject *PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status);
 
33
static void PLy_spi_exception_set(PyObject *excclass, ErrorData *edata);
 
34
 
 
35
 
 
36
/* prepare(query="select * from foo")
 
37
 * prepare(query="select * from foo where bar = $1", params=["text"])
 
38
 * prepare(query="select * from foo where bar = $1", params=["text"], limit=5)
 
39
 */
 
40
PyObject *
 
41
PLy_spi_prepare(PyObject *self, PyObject *args)
 
42
{
 
43
        PLyPlanObject *plan;
 
44
        PyObject   *list = NULL;
 
45
        PyObject   *volatile optr = NULL;
 
46
        char       *query;
 
47
        volatile MemoryContext oldcontext;
 
48
        volatile ResourceOwner oldowner;
 
49
        volatile int nargs;
 
50
 
 
51
        if (!PyArg_ParseTuple(args, "s|O", &query, &list))
 
52
                return NULL;
 
53
 
 
54
        if (list && (!PySequence_Check(list)))
 
55
        {
 
56
                PLy_exception_set(PyExc_TypeError,
 
57
                                           "second argument of plpy.prepare must be a sequence");
 
58
                return NULL;
 
59
        }
 
60
 
 
61
        if ((plan = (PLyPlanObject *) PLy_plan_new()) == NULL)
 
62
                return NULL;
 
63
 
 
64
        nargs = list ? PySequence_Length(list) : 0;
 
65
 
 
66
        plan->nargs = nargs;
 
67
        plan->types = nargs ? PLy_malloc(sizeof(Oid) * nargs) : NULL;
 
68
        plan->values = nargs ? PLy_malloc(sizeof(Datum) * nargs) : NULL;
 
69
        plan->args = nargs ? PLy_malloc(sizeof(PLyTypeInfo) * nargs) : NULL;
 
70
 
 
71
        oldcontext = CurrentMemoryContext;
 
72
        oldowner = CurrentResourceOwner;
 
73
 
 
74
        PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
75
 
 
76
        PG_TRY();
 
77
        {
 
78
                int                     i;
 
79
 
 
80
                /*
 
81
                 * the other loop might throw an exception, if PLyTypeInfo member
 
82
                 * isn't properly initialized the Py_DECREF(plan) will go boom
 
83
                 */
 
84
                for (i = 0; i < nargs; i++)
 
85
                {
 
86
                        PLy_typeinfo_init(&plan->args[i]);
 
87
                        plan->values[i] = PointerGetDatum(NULL);
 
88
                }
 
89
 
 
90
                for (i = 0; i < nargs; i++)
 
91
                {
 
92
                        char       *sptr;
 
93
                        HeapTuple       typeTup;
 
94
                        Oid                     typeId;
 
95
                        int32           typmod;
 
96
                        Form_pg_type typeStruct;
 
97
 
 
98
                        optr = PySequence_GetItem(list, i);
 
99
                        if (PyString_Check(optr))
 
100
                                sptr = PyString_AsString(optr);
 
101
                        else if (PyUnicode_Check(optr))
 
102
                                sptr = PLyUnicode_AsString(optr);
 
103
                        else
 
104
                        {
 
105
                                ereport(ERROR,
 
106
                                                (errmsg("plpy.prepare: type name at ordinal position %d is not a string", i)));
 
107
                                sptr = NULL;    /* keep compiler quiet */
 
108
                        }
 
109
 
 
110
                        /********************************************************
 
111
                         * Resolve argument type names and then look them up by
 
112
                         * oid in the system cache, and remember the required
 
113
                         *information for input conversion.
 
114
                         ********************************************************/
 
115
 
 
116
                        parseTypeString(sptr, &typeId, &typmod);
 
117
 
 
118
                        typeTup = SearchSysCache1(TYPEOID,
 
119
                                                                          ObjectIdGetDatum(typeId));
 
120
                        if (!HeapTupleIsValid(typeTup))
 
121
                                elog(ERROR, "cache lookup failed for type %u", typeId);
 
122
 
 
123
                        Py_DECREF(optr);
 
124
 
 
125
                        /*
 
126
                         * set optr to NULL, so we won't try to unref it again in case of
 
127
                         * an error
 
128
                         */
 
129
                        optr = NULL;
 
130
 
 
131
                        plan->types[i] = typeId;
 
132
                        typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
133
                        if (typeStruct->typtype != TYPTYPE_COMPOSITE)
 
134
                                PLy_output_datum_func(&plan->args[i], typeTup);
 
135
                        else
 
136
                                ereport(ERROR,
 
137
                                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
138
                                   errmsg("plpy.prepare does not support composite types")));
 
139
                        ReleaseSysCache(typeTup);
 
140
                }
 
141
 
 
142
                pg_verifymbstr(query, strlen(query), false);
 
143
                plan->plan = SPI_prepare(query, plan->nargs, plan->types);
 
144
                if (plan->plan == NULL)
 
145
                        elog(ERROR, "SPI_prepare failed: %s",
 
146
                                 SPI_result_code_string(SPI_result));
 
147
 
 
148
                /* transfer plan from procCxt to topCxt */
 
149
                if (SPI_keepplan(plan->plan))
 
150
                        elog(ERROR, "SPI_keepplan failed");
 
151
 
 
152
                PLy_spi_subtransaction_commit(oldcontext, oldowner);
 
153
        }
 
154
        PG_CATCH();
 
155
        {
 
156
                Py_DECREF(plan);
 
157
                Py_XDECREF(optr);
 
158
 
 
159
                PLy_spi_subtransaction_abort(oldcontext, oldowner);
 
160
                return NULL;
 
161
        }
 
162
        PG_END_TRY();
 
163
 
 
164
        Assert(plan->plan != NULL);
 
165
        return (PyObject *) plan;
 
166
}
 
167
 
 
168
/* execute(query="select * from foo", limit=5)
 
169
 * execute(plan=plan, values=(foo, bar), limit=5)
 
170
 */
 
171
PyObject *
 
172
PLy_spi_execute(PyObject *self, PyObject *args)
 
173
{
 
174
        char       *query;
 
175
        PyObject   *plan;
 
176
        PyObject   *list = NULL;
 
177
        long            limit = 0;
 
178
 
 
179
        if (PyArg_ParseTuple(args, "s|l", &query, &limit))
 
180
                return PLy_spi_execute_query(query, limit);
 
181
 
 
182
        PyErr_Clear();
 
183
 
 
184
        if (PyArg_ParseTuple(args, "O|Ol", &plan, &list, &limit) &&
 
185
                is_PLyPlanObject(plan))
 
186
                return PLy_spi_execute_plan(plan, list, limit);
 
187
 
 
188
        PLy_exception_set(PLy_exc_error, "plpy.execute expected a query or a plan");
 
189
        return NULL;
 
190
}
 
191
 
 
192
static PyObject *
 
193
PLy_spi_execute_plan(PyObject *ob, PyObject *list, long limit)
 
194
{
 
195
        volatile int nargs;
 
196
        int                     i,
 
197
                                rv;
 
198
        PLyPlanObject *plan;
 
199
        volatile MemoryContext oldcontext;
 
200
        volatile ResourceOwner oldowner;
 
201
        PyObject   *ret;
 
202
 
 
203
        if (list != NULL)
 
204
        {
 
205
                if (!PySequence_Check(list) || PyString_Check(list) || PyUnicode_Check(list))
 
206
                {
 
207
                        PLy_exception_set(PyExc_TypeError, "plpy.execute takes a sequence as its second argument");
 
208
                        return NULL;
 
209
                }
 
210
                nargs = PySequence_Length(list);
 
211
        }
 
212
        else
 
213
                nargs = 0;
 
214
 
 
215
        plan = (PLyPlanObject *) ob;
 
216
 
 
217
        if (nargs != plan->nargs)
 
218
        {
 
219
                char       *sv;
 
220
                PyObject   *so = PyObject_Str(list);
 
221
 
 
222
                if (!so)
 
223
                        PLy_elog(ERROR, "could not execute plan");
 
224
                sv = PyString_AsString(so);
 
225
                PLy_exception_set_plural(PyExc_TypeError,
 
226
                                                          "Expected sequence of %d argument, got %d: %s",
 
227
                                                         "Expected sequence of %d arguments, got %d: %s",
 
228
                                                                 plan->nargs,
 
229
                                                                 plan->nargs, nargs, sv);
 
230
                Py_DECREF(so);
 
231
 
 
232
                return NULL;
 
233
        }
 
234
 
 
235
        oldcontext = CurrentMemoryContext;
 
236
        oldowner = CurrentResourceOwner;
 
237
 
 
238
        PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
239
 
 
240
        PG_TRY();
 
241
        {
 
242
                PLyExecutionContext *exec_ctx = PLy_current_execution_context();
 
243
                char       *volatile nulls;
 
244
                volatile int j;
 
245
 
 
246
                if (nargs > 0)
 
247
                        nulls = palloc(nargs * sizeof(char));
 
248
                else
 
249
                        nulls = NULL;
 
250
 
 
251
                for (j = 0; j < nargs; j++)
 
252
                {
 
253
                        PyObject   *elem;
 
254
 
 
255
                        elem = PySequence_GetItem(list, j);
 
256
                        if (elem != Py_None)
 
257
                        {
 
258
                                PG_TRY();
 
259
                                {
 
260
                                        plan->values[j] =
 
261
                                                plan->args[j].out.d.func(&(plan->args[j].out.d),
 
262
                                                                                                 -1,
 
263
                                                                                                 elem);
 
264
                                }
 
265
                                PG_CATCH();
 
266
                                {
 
267
                                        Py_DECREF(elem);
 
268
                                        PG_RE_THROW();
 
269
                                }
 
270
                                PG_END_TRY();
 
271
 
 
272
                                Py_DECREF(elem);
 
273
                                nulls[j] = ' ';
 
274
                        }
 
275
                        else
 
276
                        {
 
277
                                Py_DECREF(elem);
 
278
                                plan->values[j] =
 
279
                                        InputFunctionCall(&(plan->args[j].out.d.typfunc),
 
280
                                                                          NULL,
 
281
                                                                          plan->args[j].out.d.typioparam,
 
282
                                                                          -1);
 
283
                                nulls[j] = 'n';
 
284
                        }
 
285
                }
 
286
 
 
287
                rv = SPI_execute_plan(plan->plan, plan->values, nulls,
 
288
                                                          exec_ctx->curr_proc->fn_readonly, limit);
 
289
                ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 
290
 
 
291
                if (nargs > 0)
 
292
                        pfree(nulls);
 
293
 
 
294
                PLy_spi_subtransaction_commit(oldcontext, oldowner);
 
295
        }
 
296
        PG_CATCH();
 
297
        {
 
298
                int                     k;
 
299
 
 
300
                /*
 
301
                 * cleanup plan->values array
 
302
                 */
 
303
                for (k = 0; k < nargs; k++)
 
304
                {
 
305
                        if (!plan->args[k].out.d.typbyval &&
 
306
                                (plan->values[k] != PointerGetDatum(NULL)))
 
307
                        {
 
308
                                pfree(DatumGetPointer(plan->values[k]));
 
309
                                plan->values[k] = PointerGetDatum(NULL);
 
310
                        }
 
311
                }
 
312
 
 
313
                PLy_spi_subtransaction_abort(oldcontext, oldowner);
 
314
                return NULL;
 
315
        }
 
316
        PG_END_TRY();
 
317
 
 
318
        for (i = 0; i < nargs; i++)
 
319
        {
 
320
                if (!plan->args[i].out.d.typbyval &&
 
321
                        (plan->values[i] != PointerGetDatum(NULL)))
 
322
                {
 
323
                        pfree(DatumGetPointer(plan->values[i]));
 
324
                        plan->values[i] = PointerGetDatum(NULL);
 
325
                }
 
326
        }
 
327
 
 
328
        if (rv < 0)
 
329
        {
 
330
                PLy_exception_set(PLy_exc_spi_error,
 
331
                                                  "SPI_execute_plan failed: %s",
 
332
                                                  SPI_result_code_string(rv));
 
333
                return NULL;
 
334
        }
 
335
 
 
336
        return ret;
 
337
}
 
338
 
 
339
static PyObject *
 
340
PLy_spi_execute_query(char *query, long limit)
 
341
{
 
342
        int                     rv;
 
343
        volatile MemoryContext oldcontext;
 
344
        volatile ResourceOwner oldowner;
 
345
        PyObject   *ret = NULL;
 
346
 
 
347
        oldcontext = CurrentMemoryContext;
 
348
        oldowner = CurrentResourceOwner;
 
349
 
 
350
        PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
351
 
 
352
        PG_TRY();
 
353
        {
 
354
                PLyExecutionContext *exec_ctx = PLy_current_execution_context();
 
355
 
 
356
                pg_verifymbstr(query, strlen(query), false);
 
357
                rv = SPI_execute(query, exec_ctx->curr_proc->fn_readonly, limit);
 
358
                ret = PLy_spi_execute_fetch_result(SPI_tuptable, SPI_processed, rv);
 
359
 
 
360
                PLy_spi_subtransaction_commit(oldcontext, oldowner);
 
361
        }
 
362
        PG_CATCH();
 
363
        {
 
364
                PLy_spi_subtransaction_abort(oldcontext, oldowner);
 
365
                return NULL;
 
366
        }
 
367
        PG_END_TRY();
 
368
 
 
369
        if (rv < 0)
 
370
        {
 
371
                Py_XDECREF(ret);
 
372
                PLy_exception_set(PLy_exc_spi_error,
 
373
                                                  "SPI_execute failed: %s",
 
374
                                                  SPI_result_code_string(rv));
 
375
                return NULL;
 
376
        }
 
377
 
 
378
        return ret;
 
379
}
 
380
 
 
381
static PyObject *
 
382
PLy_spi_execute_fetch_result(SPITupleTable *tuptable, int rows, int status)
 
383
{
 
384
        PLyResultObject *result;
 
385
        volatile MemoryContext oldcontext;
 
386
 
 
387
        result = (PLyResultObject *) PLy_result_new();
 
388
        Py_DECREF(result->status);
 
389
        result->status = PyInt_FromLong(status);
 
390
 
 
391
        if (status > 0 && tuptable == NULL)
 
392
        {
 
393
                Py_DECREF(result->nrows);
 
394
                result->nrows = PyInt_FromLong(rows);
 
395
        }
 
396
        else if (status > 0 && tuptable != NULL)
 
397
        {
 
398
                PLyTypeInfo args;
 
399
                int                     i;
 
400
 
 
401
                Py_DECREF(result->nrows);
 
402
                result->nrows = PyInt_FromLong(rows);
 
403
                PLy_typeinfo_init(&args);
 
404
 
 
405
                oldcontext = CurrentMemoryContext;
 
406
                PG_TRY();
 
407
                {
 
408
                        MemoryContext oldcontext2;
 
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.
 
415
                         */
 
416
                        oldcontext2 = MemoryContextSwitchTo(TopMemoryContext);
 
417
                        result->tupdesc = CreateTupleDescCopy(tuptable->tupdesc);
 
418
                        MemoryContextSwitchTo(oldcontext2);
 
419
 
 
420
                        if (rows)
 
421
                        {
 
422
                                Py_DECREF(result->rows);
 
423
                                result->rows = PyList_New(rows);
 
424
 
 
425
                                PLy_input_tuple_funcs(&args, tuptable->tupdesc);
 
426
                                for (i = 0; i < rows; i++)
 
427
                                {
 
428
                                        PyObject   *row = PLyDict_FromTuple(&args, tuptable->vals[i],
 
429
                                                                                                                tuptable->tupdesc);
 
430
 
 
431
                                        PyList_SetItem(result->rows, i, row);
 
432
                                }
 
433
                        }
 
434
                }
 
435
                PG_CATCH();
 
436
                {
 
437
                        MemoryContextSwitchTo(oldcontext);
 
438
                        if (!PyErr_Occurred())
 
439
                                PLy_exception_set(PLy_exc_error,
 
440
                                           "unrecognized error in PLy_spi_execute_fetch_result");
 
441
                        PLy_typeinfo_dealloc(&args);
 
442
                        SPI_freetuptable(tuptable);
 
443
                        Py_DECREF(result);
 
444
                        return NULL;
 
445
                }
 
446
                PG_END_TRY();
 
447
 
 
448
                PLy_typeinfo_dealloc(&args);
 
449
                SPI_freetuptable(tuptable);
 
450
        }
 
451
 
 
452
        return (PyObject *) result;
 
453
}
 
454
 
 
455
/*
 
456
 * Utilities for running SPI functions in subtransactions.
 
457
 *
 
458
 * Usage:
 
459
 *
 
460
 *      MemoryContext oldcontext = CurrentMemoryContext;
 
461
 *      ResourceOwner oldowner = CurrentResourceOwner;
 
462
 *
 
463
 *      PLy_spi_subtransaction_begin(oldcontext, oldowner);
 
464
 *      PG_TRY();
 
465
 *      {
 
466
 *              <call SPI functions>
 
467
 *              PLy_spi_subtransaction_commit(oldcontext, oldowner);
 
468
 *      }
 
469
 *      PG_CATCH();
 
470
 *      {
 
471
 *              <do cleanup>
 
472
 *              PLy_spi_subtransaction_abort(oldcontext, oldowner);
 
473
 *              return NULL;
 
474
 *      }
 
475
 *      PG_END_TRY();
 
476
 *
 
477
 * These utilities take care of restoring connection to the SPI manager and
 
478
 * setting a Python exception in case of an abort.
 
479
 */
 
480
void
 
481
PLy_spi_subtransaction_begin(MemoryContext oldcontext, ResourceOwner oldowner)
 
482
{
 
483
        BeginInternalSubTransaction(NULL);
 
484
        /* Want to run inside function's memory context */
 
485
        MemoryContextSwitchTo(oldcontext);
 
486
}
 
487
 
 
488
void
 
489
PLy_spi_subtransaction_commit(MemoryContext oldcontext, ResourceOwner oldowner)
 
490
{
 
491
        /* Commit the inner transaction, return to outer xact context */
 
492
        ReleaseCurrentSubTransaction();
 
493
        MemoryContextSwitchTo(oldcontext);
 
494
        CurrentResourceOwner = oldowner;
 
495
 
 
496
        /*
 
497
         * AtEOSubXact_SPI() should not have popped any SPI context, but just in
 
498
         * case it did, make sure we remain connected.
 
499
         */
 
500
        SPI_restore_connection();
 
501
}
 
502
 
 
503
void
 
504
PLy_spi_subtransaction_abort(MemoryContext oldcontext, ResourceOwner oldowner)
 
505
{
 
506
        ErrorData  *edata;
 
507
        PLyExceptionEntry *entry;
 
508
        PyObject   *exc;
 
509
 
 
510
        /* Save error info */
 
511
        MemoryContextSwitchTo(oldcontext);
 
512
        edata = CopyErrorData();
 
513
        FlushErrorState();
 
514
 
 
515
        /* Abort the inner transaction */
 
516
        RollbackAndReleaseCurrentSubTransaction();
 
517
        MemoryContextSwitchTo(oldcontext);
 
518
        CurrentResourceOwner = oldowner;
 
519
 
 
520
        /*
 
521
         * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
 
522
         * have left us in a disconnected state.  We need this hack to return to
 
523
         * connected state.
 
524
         */
 
525
        SPI_restore_connection();
 
526
 
 
527
        /* Look up the correct exception */
 
528
        entry = hash_search(PLy_spi_exceptions, &(edata->sqlerrcode),
 
529
                                                HASH_FIND, NULL);
 
530
        /* We really should find it, but just in case have a fallback */
 
531
        Assert(entry != NULL);
 
532
        exc = entry ? entry->exc : PLy_exc_spi_error;
 
533
        /* Make Python raise the exception */
 
534
        PLy_spi_exception_set(exc, edata);
 
535
        FreeErrorData(edata);
 
536
}
 
537
 
 
538
/*
 
539
 * Raise a SPIError, passing in it more error details, like the
 
540
 * internal query and error position.
 
541
 */
 
542
static void
 
543
PLy_spi_exception_set(PyObject *excclass, ErrorData *edata)
 
544
{
 
545
        PyObject   *args = NULL;
 
546
        PyObject   *spierror = NULL;
 
547
        PyObject   *spidata = NULL;
 
548
 
 
549
        args = Py_BuildValue("(s)", edata->message);
 
550
        if (!args)
 
551
                goto failure;
 
552
 
 
553
        /* create a new SPI exception with the error message as the parameter */
 
554
        spierror = PyObject_CallObject(excclass, args);
 
555
        if (!spierror)
 
556
                goto failure;
 
557
 
 
558
        spidata = Py_BuildValue("(izzzi)", edata->sqlerrcode, edata->detail, edata->hint,
 
559
                                                        edata->internalquery, edata->internalpos);
 
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);
 
569
        Py_DECREF(spierror);
 
570
        Py_DECREF(spidata);
 
571
        return;
 
572
 
 
573
failure:
 
574
        Py_XDECREF(args);
 
575
        Py_XDECREF(spierror);
 
576
        Py_XDECREF(spidata);
 
577
        elog(ERROR, "could not convert SPI error to Python exception");
 
578
}