~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/executor/spi.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * spi.c
 
4
 *                              Server Programming Interface
 
5
 *
 
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *        $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.133.4.1 2005-02-10 20:36:48 tgl Exp $
 
12
 *
 
13
 *-------------------------------------------------------------------------
 
14
 */
 
15
#include "postgres.h"
 
16
 
 
17
#include "access/printtup.h"
 
18
#include "catalog/heap.h"
 
19
#include "commands/trigger.h"
 
20
#include "executor/spi_priv.h"
 
21
#include "tcop/tcopprot.h"
 
22
#include "utils/lsyscache.h"
 
23
#include "utils/typcache.h"
 
24
 
 
25
 
 
26
uint32          SPI_processed = 0;
 
27
Oid                     SPI_lastoid = InvalidOid;
 
28
SPITupleTable *SPI_tuptable = NULL;
 
29
int                     SPI_result;
 
30
 
 
31
static _SPI_connection *_SPI_stack = NULL;
 
32
static _SPI_connection *_SPI_current = NULL;
 
33
static int      _SPI_stack_depth = 0;           /* allocated size of _SPI_stack */
 
34
static int      _SPI_connected = -1;
 
35
static int      _SPI_curid = -1;
 
36
 
 
37
static void _SPI_prepare_plan(const char *src, _SPI_plan *plan);
 
38
 
 
39
static int _SPI_execute_plan(_SPI_plan *plan,
 
40
                                                         Datum *Values, const char *Nulls,
 
41
                                                         Snapshot snapshot, Snapshot crosscheck_snapshot,
 
42
                                                         bool read_only, int tcount);
 
43
 
 
44
static int _SPI_pquery(QueryDesc *queryDesc, int tcount);
 
45
 
 
46
static void _SPI_error_callback(void *arg);
 
47
 
 
48
static void _SPI_cursor_operation(Portal portal, bool forward, int count,
 
49
                                          DestReceiver *dest);
 
50
 
 
51
static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
 
52
 
 
53
static int      _SPI_begin_call(bool execmem);
 
54
static int      _SPI_end_call(bool procmem);
 
55
static MemoryContext _SPI_execmem(void);
 
56
static MemoryContext _SPI_procmem(void);
 
57
static bool _SPI_checktuples(void);
 
58
 
 
59
 
 
60
/* =================== interface functions =================== */
 
61
 
 
62
int
 
63
SPI_connect(void)
 
64
{
 
65
        int                     newdepth;
 
66
 
 
67
        /*
 
68
         * When procedure called by Executor _SPI_curid expected to be equal
 
69
         * to _SPI_connected
 
70
         */
 
71
        if (_SPI_curid != _SPI_connected)
 
72
                return SPI_ERROR_CONNECT;
 
73
 
 
74
        if (_SPI_stack == NULL)
 
75
        {
 
76
                if (_SPI_connected != -1 || _SPI_stack_depth != 0)
 
77
                        elog(ERROR, "SPI stack corrupted");
 
78
                newdepth = 16;
 
79
                _SPI_stack = (_SPI_connection *)
 
80
                        MemoryContextAlloc(TopTransactionContext,
 
81
                                                           newdepth * sizeof(_SPI_connection));
 
82
                _SPI_stack_depth = newdepth;
 
83
        }
 
84
        else
 
85
        {
 
86
                if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
 
87
                        elog(ERROR, "SPI stack corrupted");
 
88
                if (_SPI_stack_depth == _SPI_connected + 1)
 
89
                {
 
90
                        newdepth = _SPI_stack_depth * 2;
 
91
                        _SPI_stack = (_SPI_connection *)
 
92
                                repalloc(_SPI_stack,
 
93
                                                 newdepth * sizeof(_SPI_connection));
 
94
                        _SPI_stack_depth = newdepth;
 
95
                }
 
96
        }
 
97
 
 
98
        /*
 
99
         * We're entering procedure where _SPI_curid == _SPI_connected - 1
 
100
         */
 
101
        _SPI_connected++;
 
102
        Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
 
103
 
 
104
        _SPI_current = &(_SPI_stack[_SPI_connected]);
 
105
        _SPI_current->processed = 0;
 
106
        _SPI_current->tuptable = NULL;
 
107
        _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
 
108
        _SPI_current->execCxt = NULL;
 
109
        _SPI_current->connectSubid = GetCurrentSubTransactionId();
 
110
 
 
111
        /*
 
112
         * Create memory contexts for this procedure
 
113
         *
 
114
         * XXX it would be better to use PortalContext as the parent context, but
 
115
         * we may not be inside a portal (consider deferred-trigger
 
116
         * execution).  Perhaps CurTransactionContext would do?  For now it
 
117
         * doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
 
118
         */
 
119
        _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
 
120
                                                                                                  "SPI Proc",
 
121
                                                                                                ALLOCSET_DEFAULT_MINSIZE,
 
122
                                                                                           ALLOCSET_DEFAULT_INITSIZE,
 
123
                                                                                           ALLOCSET_DEFAULT_MAXSIZE);
 
124
        _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
 
125
                                                                                                  "SPI Exec",
 
126
                                                                                                ALLOCSET_DEFAULT_MINSIZE,
 
127
                                                                                           ALLOCSET_DEFAULT_INITSIZE,
 
128
                                                                                           ALLOCSET_DEFAULT_MAXSIZE);
 
129
        /* ... and switch to procedure's context */
 
130
        _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
 
131
 
 
132
        return SPI_OK_CONNECT;
 
133
}
 
134
 
 
135
int
 
136
SPI_finish(void)
 
137
{
 
138
        int                     res;
 
139
 
 
140
        res = _SPI_begin_call(false);           /* live in procedure memory */
 
141
        if (res < 0)
 
142
                return res;
 
143
 
 
144
        /* Restore memory context as it was before procedure call */
 
145
        MemoryContextSwitchTo(_SPI_current->savedcxt);
 
146
 
 
147
        /* Release memory used in procedure call */
 
148
        MemoryContextDelete(_SPI_current->execCxt);
 
149
        _SPI_current->execCxt = NULL;
 
150
        MemoryContextDelete(_SPI_current->procCxt);
 
151
        _SPI_current->procCxt = NULL;
 
152
 
 
153
        /*
 
154
         * Reset result variables, especially SPI_tuptable which is probably
 
155
         * pointing at a just-deleted tuptable
 
156
         */
 
157
        SPI_processed = 0;
 
158
        SPI_lastoid = InvalidOid;
 
159
        SPI_tuptable = NULL;
 
160
 
 
161
        /*
 
162
         * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
 
163
         * closing connection to SPI and returning to upper Executor and so
 
164
         * _SPI_connected must be equal to _SPI_curid.
 
165
         */
 
166
        _SPI_connected--;
 
167
        _SPI_curid--;
 
168
        if (_SPI_connected == -1)
 
169
                _SPI_current = NULL;
 
170
        else
 
171
                _SPI_current = &(_SPI_stack[_SPI_connected]);
 
172
 
 
173
        return SPI_OK_FINISH;
 
174
}
 
175
 
 
176
/*
 
177
 * Clean up SPI state at transaction commit or abort.
 
178
 */
 
179
void
 
180
AtEOXact_SPI(bool isCommit)
 
181
{
 
182
        /*
 
183
         * Note that memory contexts belonging to SPI stack entries will be
 
184
         * freed automatically, so we can ignore them here.  We just need to
 
185
         * restore our static variables to initial state.
 
186
         */
 
187
        if (isCommit && _SPI_connected != -1)
 
188
                ereport(WARNING,
 
189
                                (errcode(ERRCODE_WARNING),
 
190
                                 errmsg("transaction left non-empty SPI stack"),
 
191
                                 errhint("Check for missing \"SPI_finish\" calls.")));
 
192
 
 
193
        _SPI_current = _SPI_stack = NULL;
 
194
        _SPI_stack_depth = 0;
 
195
        _SPI_connected = _SPI_curid = -1;
 
196
        SPI_processed = 0;
 
197
        SPI_lastoid = InvalidOid;
 
198
        SPI_tuptable = NULL;
 
199
}
 
200
 
 
201
/*
 
202
 * Clean up SPI state at subtransaction commit or abort.
 
203
 *
 
204
 * During commit, there shouldn't be any unclosed entries remaining from
 
205
 * the current subtransaction; we emit a warning if any are found.
 
206
 */
 
207
void
 
208
AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
 
209
{
 
210
        bool            found = false;
 
211
 
 
212
        while (_SPI_connected >= 0)
 
213
        {
 
214
                _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
 
215
 
 
216
                if (connection->connectSubid != mySubid)
 
217
                        break;                          /* couldn't be any underneath it either */
 
218
 
 
219
                found = true;
 
220
 
 
221
                /*
 
222
                 * Release procedure memory explicitly (see note in SPI_connect)
 
223
                 */
 
224
                if (connection->execCxt)
 
225
                {
 
226
                        MemoryContextDelete(connection->execCxt);
 
227
                        connection->execCxt = NULL;
 
228
                }
 
229
                if (connection->procCxt)
 
230
                {
 
231
                        MemoryContextDelete(connection->procCxt);
 
232
                        connection->procCxt = NULL;
 
233
                }
 
234
 
 
235
                /*
 
236
                 * Pop the stack entry and reset global variables.      Unlike
 
237
                 * SPI_finish(), we don't risk switching to memory contexts that
 
238
                 * might be already gone.
 
239
                 */
 
240
                _SPI_connected--;
 
241
                _SPI_curid = _SPI_connected;
 
242
                if (_SPI_connected == -1)
 
243
                        _SPI_current = NULL;
 
244
                else
 
245
                        _SPI_current = &(_SPI_stack[_SPI_connected]);
 
246
                SPI_processed = 0;
 
247
                SPI_lastoid = InvalidOid;
 
248
                SPI_tuptable = NULL;
 
249
        }
 
250
 
 
251
        if (found && isCommit)
 
252
                ereport(WARNING,
 
253
                                (errcode(ERRCODE_WARNING),
 
254
                                 errmsg("subtransaction left non-empty SPI stack"),
 
255
                                 errhint("Check for missing \"SPI_finish\" calls.")));
 
256
}
 
257
 
 
258
 
 
259
/* Pushes SPI stack to allow recursive SPI calls */
 
260
void
 
261
SPI_push(void)
 
262
{
 
263
        _SPI_curid++;
 
264
}
 
265
 
 
266
/* Pops SPI stack to allow recursive SPI calls */
 
267
void
 
268
SPI_pop(void)
 
269
{
 
270
        _SPI_curid--;
 
271
}
 
272
 
 
273
/* Restore state of SPI stack after aborting a subtransaction */
 
274
void
 
275
SPI_restore_connection(void)
 
276
{
 
277
        Assert(_SPI_connected >= 0);
 
278
        _SPI_curid = _SPI_connected - 1;
 
279
}
 
280
 
 
281
/* Parse, plan, and execute a querystring */
 
282
int
 
283
SPI_execute(const char *src, bool read_only, int tcount)
 
284
{
 
285
        _SPI_plan       plan;
 
286
        int                     res;
 
287
 
 
288
        if (src == NULL || tcount < 0)
 
289
                return SPI_ERROR_ARGUMENT;
 
290
 
 
291
        res = _SPI_begin_call(true);
 
292
        if (res < 0)
 
293
                return res;
 
294
 
 
295
        plan.plancxt = NULL;            /* doesn't have own context */
 
296
        plan.query = src;
 
297
        plan.nargs = 0;
 
298
        plan.argtypes = NULL;
 
299
 
 
300
        _SPI_prepare_plan(src, &plan);
 
301
 
 
302
        res = _SPI_execute_plan(&plan, NULL, NULL,
 
303
                                                        InvalidSnapshot, InvalidSnapshot,
 
304
                                                        read_only, tcount);
 
305
 
 
306
        _SPI_end_call(true);
 
307
        return res;
 
308
}
 
309
 
 
310
/* Obsolete version of SPI_execute */
 
311
int
 
312
SPI_exec(const char *src, int tcount)
 
313
{
 
314
        return SPI_execute(src, false, tcount);
 
315
}
 
316
 
 
317
/* Execute a previously prepared plan */
 
318
int
 
319
SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
 
320
                                 bool read_only, int tcount)
 
321
{
 
322
        int                     res;
 
323
 
 
324
        if (plan == NULL || tcount < 0)
 
325
                return SPI_ERROR_ARGUMENT;
 
326
 
 
327
        if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
 
328
                return SPI_ERROR_PARAM;
 
329
 
 
330
        res = _SPI_begin_call(true);
 
331
        if (res < 0)
 
332
                return res;
 
333
 
 
334
        res = _SPI_execute_plan((_SPI_plan *) plan,
 
335
                                                        Values, Nulls,
 
336
                                                        InvalidSnapshot, InvalidSnapshot,
 
337
                                                        read_only, tcount);
 
338
 
 
339
        _SPI_end_call(true);
 
340
        return res;
 
341
}
 
342
 
 
343
/* Obsolete version of SPI_execute_plan */
 
344
int
 
345
SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
 
346
{
 
347
        return SPI_execute_plan(plan, Values, Nulls, false, tcount);
 
348
}
 
349
 
 
350
/*
 
351
 * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
 
352
 * the caller to specify exactly which snapshots to use.  This is currently
 
353
 * not documented in spi.sgml because it is only intended for use by RI
 
354
 * triggers.
 
355
 *
 
356
 * Passing snapshot == InvalidSnapshot will select the normal behavior of
 
357
 * fetching a new snapshot for each query.
 
358
 */
 
359
int
 
360
SPI_execute_snapshot(void *plan,
 
361
                                         Datum *Values, const char *Nulls,
 
362
                                         Snapshot snapshot, Snapshot crosscheck_snapshot,
 
363
                                         bool read_only, int tcount)
 
364
{
 
365
        int                     res;
 
366
 
 
367
        if (plan == NULL || tcount < 0)
 
368
                return SPI_ERROR_ARGUMENT;
 
369
 
 
370
        if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
 
371
                return SPI_ERROR_PARAM;
 
372
 
 
373
        res = _SPI_begin_call(true);
 
374
        if (res < 0)
 
375
                return res;
 
376
 
 
377
        res = _SPI_execute_plan((_SPI_plan *) plan,
 
378
                                                        Values, Nulls,
 
379
                                                        snapshot, crosscheck_snapshot,
 
380
                                                        read_only, tcount);
 
381
 
 
382
        _SPI_end_call(true);
 
383
        return res;
 
384
}
 
385
 
 
386
void *
 
387
SPI_prepare(const char *src, int nargs, Oid *argtypes)
 
388
{
 
389
        _SPI_plan       plan;
 
390
        _SPI_plan  *result;
 
391
 
 
392
        if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
 
393
        {
 
394
                SPI_result = SPI_ERROR_ARGUMENT;
 
395
                return NULL;
 
396
        }
 
397
 
 
398
        SPI_result = _SPI_begin_call(true);
 
399
        if (SPI_result < 0)
 
400
                return NULL;
 
401
 
 
402
        plan.plancxt = NULL;            /* doesn't have own context */
 
403
        plan.query = src;
 
404
        plan.nargs = nargs;
 
405
        plan.argtypes = argtypes;
 
406
 
 
407
        _SPI_prepare_plan(src, &plan);
 
408
 
 
409
        /* copy plan to procedure context */
 
410
        result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
 
411
 
 
412
        _SPI_end_call(true);
 
413
 
 
414
        return (void *) result;
 
415
}
 
416
 
 
417
void *
 
418
SPI_saveplan(void *plan)
 
419
{
 
420
        _SPI_plan  *newplan;
 
421
 
 
422
        if (plan == NULL)
 
423
        {
 
424
                SPI_result = SPI_ERROR_ARGUMENT;
 
425
                return NULL;
 
426
        }
 
427
 
 
428
        SPI_result = _SPI_begin_call(false);            /* don't change context */
 
429
        if (SPI_result < 0)
 
430
                return NULL;
 
431
 
 
432
        newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
 
433
 
 
434
        _SPI_curid--;
 
435
        SPI_result = 0;
 
436
 
 
437
        return (void *) newplan;
 
438
}
 
439
 
 
440
int
 
441
SPI_freeplan(void *plan)
 
442
{
 
443
        _SPI_plan  *spiplan = (_SPI_plan *) plan;
 
444
 
 
445
        if (plan == NULL)
 
446
                return SPI_ERROR_ARGUMENT;
 
447
 
 
448
        MemoryContextDelete(spiplan->plancxt);
 
449
        return 0;
 
450
}
 
451
 
 
452
HeapTuple
 
453
SPI_copytuple(HeapTuple tuple)
 
454
{
 
455
        MemoryContext oldcxt = NULL;
 
456
        HeapTuple       ctuple;
 
457
 
 
458
        if (tuple == NULL)
 
459
        {
 
460
                SPI_result = SPI_ERROR_ARGUMENT;
 
461
                return NULL;
 
462
        }
 
463
 
 
464
        if (_SPI_curid + 1 == _SPI_connected)           /* connected */
 
465
        {
 
466
                if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
 
467
                        elog(ERROR, "SPI stack corrupted");
 
468
                oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
 
469
        }
 
470
 
 
471
        ctuple = heap_copytuple(tuple);
 
472
 
 
473
        if (oldcxt)
 
474
                MemoryContextSwitchTo(oldcxt);
 
475
 
 
476
        return ctuple;
 
477
}
 
478
 
 
479
HeapTupleHeader
 
480
SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
 
481
{
 
482
        MemoryContext oldcxt = NULL;
 
483
        HeapTupleHeader dtup;
 
484
 
 
485
        if (tuple == NULL || tupdesc == NULL)
 
486
        {
 
487
                SPI_result = SPI_ERROR_ARGUMENT;
 
488
                return NULL;
 
489
        }
 
490
 
 
491
        /* For RECORD results, make sure a typmod has been assigned */
 
492
        if (tupdesc->tdtypeid == RECORDOID &&
 
493
                tupdesc->tdtypmod < 0)
 
494
                assign_record_type_typmod(tupdesc);
 
495
 
 
496
        if (_SPI_curid + 1 == _SPI_connected)           /* connected */
 
497
        {
 
498
                if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
 
499
                        elog(ERROR, "SPI stack corrupted");
 
500
                oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
 
501
        }
 
502
 
 
503
        dtup = (HeapTupleHeader) palloc(tuple->t_len);
 
504
        memcpy((char *) dtup, (char *) tuple->t_data, tuple->t_len);
 
505
 
 
506
        HeapTupleHeaderSetDatumLength(dtup, tuple->t_len);
 
507
        HeapTupleHeaderSetTypeId(dtup, tupdesc->tdtypeid);
 
508
        HeapTupleHeaderSetTypMod(dtup, tupdesc->tdtypmod);
 
509
 
 
510
        if (oldcxt)
 
511
                MemoryContextSwitchTo(oldcxt);
 
512
 
 
513
        return dtup;
 
514
}
 
515
 
 
516
HeapTuple
 
517
SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 
518
                                Datum *Values, const char *Nulls)
 
519
{
 
520
        MemoryContext oldcxt = NULL;
 
521
        HeapTuple       mtuple;
 
522
        int                     numberOfAttributes;
 
523
        Datum      *v;
 
524
        char       *n;
 
525
        int                     i;
 
526
 
 
527
        if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
 
528
        {
 
529
                SPI_result = SPI_ERROR_ARGUMENT;
 
530
                return NULL;
 
531
        }
 
532
 
 
533
        if (_SPI_curid + 1 == _SPI_connected)           /* connected */
 
534
        {
 
535
                if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
 
536
                        elog(ERROR, "SPI stack corrupted");
 
537
                oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
 
538
        }
 
539
        SPI_result = 0;
 
540
        numberOfAttributes = rel->rd_att->natts;
 
541
        v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
 
542
        n = (char *) palloc(numberOfAttributes * sizeof(char));
 
543
 
 
544
        /* fetch old values and nulls */
 
545
        heap_deformtuple(tuple, rel->rd_att, v, n);
 
546
 
 
547
        /* replace values and nulls */
 
548
        for (i = 0; i < natts; i++)
 
549
        {
 
550
                if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
 
551
                        break;
 
552
                v[attnum[i] - 1] = Values[i];
 
553
                n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
 
554
        }
 
555
 
 
556
        if (i == natts)                         /* no errors in *attnum */
 
557
        {
 
558
                mtuple = heap_formtuple(rel->rd_att, v, n);
 
559
 
 
560
                /*
 
561
                 * copy the identification info of the old tuple: t_ctid, t_self,
 
562
                 * and OID (if any)
 
563
                 */
 
564
                mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
 
565
                mtuple->t_self = tuple->t_self;
 
566
                mtuple->t_tableOid = tuple->t_tableOid;
 
567
                if (rel->rd_att->tdhasoid)
 
568
                        HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
 
569
        }
 
570
        else
 
571
        {
 
572
                mtuple = NULL;
 
573
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
574
        }
 
575
 
 
576
        pfree(v);
 
577
        pfree(n);
 
578
 
 
579
        if (oldcxt)
 
580
                MemoryContextSwitchTo(oldcxt);
 
581
 
 
582
        return mtuple;
 
583
}
 
584
 
 
585
int
 
586
SPI_fnumber(TupleDesc tupdesc, const char *fname)
 
587
{
 
588
        int                     res;
 
589
        Form_pg_attribute sysatt;
 
590
 
 
591
        for (res = 0; res < tupdesc->natts; res++)
 
592
        {
 
593
                if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
 
594
                        return res + 1;
 
595
        }
 
596
 
 
597
        sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
 
598
        if (sysatt != NULL)
 
599
                return sysatt->attnum;
 
600
 
 
601
        /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
 
602
        return SPI_ERROR_NOATTRIBUTE;
 
603
}
 
604
 
 
605
char *
 
606
SPI_fname(TupleDesc tupdesc, int fnumber)
 
607
{
 
608
        Form_pg_attribute att;
 
609
 
 
610
        SPI_result = 0;
 
611
 
 
612
        if (fnumber > tupdesc->natts || fnumber == 0 ||
 
613
                fnumber <= FirstLowInvalidHeapAttributeNumber)
 
614
        {
 
615
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
616
                return NULL;
 
617
        }
 
618
 
 
619
        if (fnumber > 0)
 
620
                att = tupdesc->attrs[fnumber - 1];
 
621
        else
 
622
                att = SystemAttributeDefinition(fnumber, true);
 
623
 
 
624
        return pstrdup(NameStr(att->attname));
 
625
}
 
626
 
 
627
char *
 
628
SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
 
629
{
 
630
        Datum           origval,
 
631
                                val,
 
632
                                result;
 
633
        bool            isnull;
 
634
        Oid                     typoid,
 
635
                                foutoid,
 
636
                                typioparam;
 
637
        int32           typmod;
 
638
        bool            typisvarlena;
 
639
 
 
640
        SPI_result = 0;
 
641
 
 
642
        if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
 
643
                fnumber <= FirstLowInvalidHeapAttributeNumber)
 
644
        {
 
645
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
646
                return NULL;
 
647
        }
 
648
 
 
649
        origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
 
650
        if (isnull)
 
651
                return NULL;
 
652
 
 
653
        if (fnumber > 0)
 
654
        {
 
655
                typoid = tupdesc->attrs[fnumber - 1]->atttypid;
 
656
                typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
 
657
        }
 
658
        else
 
659
        {
 
660
                typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
 
661
                typmod = -1;
 
662
        }
 
663
 
 
664
        getTypeOutputInfo(typoid, &foutoid, &typioparam, &typisvarlena);
 
665
 
 
666
        /*
 
667
         * If we have a toasted datum, forcibly detoast it here to avoid
 
668
         * memory leakage inside the type's output routine.
 
669
         */
 
670
        if (typisvarlena)
 
671
                val = PointerGetDatum(PG_DETOAST_DATUM(origval));
 
672
        else
 
673
                val = origval;
 
674
 
 
675
        result = OidFunctionCall3(foutoid,
 
676
                                                          val,
 
677
                                                          ObjectIdGetDatum(typioparam),
 
678
                                                          Int32GetDatum(typmod));
 
679
 
 
680
        /* Clean up detoasted copy, if any */
 
681
        if (val != origval)
 
682
                pfree(DatumGetPointer(val));
 
683
 
 
684
        return DatumGetCString(result);
 
685
}
 
686
 
 
687
Datum
 
688
SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
 
689
{
 
690
        SPI_result = 0;
 
691
 
 
692
        if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
 
693
                fnumber <= FirstLowInvalidHeapAttributeNumber)
 
694
        {
 
695
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
696
                *isnull = true;
 
697
                return (Datum) NULL;
 
698
        }
 
699
 
 
700
        return heap_getattr(tuple, fnumber, tupdesc, isnull);
 
701
}
 
702
 
 
703
char *
 
704
SPI_gettype(TupleDesc tupdesc, int fnumber)
 
705
{
 
706
        Oid                     typoid;
 
707
        HeapTuple       typeTuple;
 
708
        char       *result;
 
709
 
 
710
        SPI_result = 0;
 
711
 
 
712
        if (fnumber > tupdesc->natts || fnumber == 0 ||
 
713
                fnumber <= FirstLowInvalidHeapAttributeNumber)
 
714
        {
 
715
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
716
                return NULL;
 
717
        }
 
718
 
 
719
        if (fnumber > 0)
 
720
                typoid = tupdesc->attrs[fnumber - 1]->atttypid;
 
721
        else
 
722
                typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
 
723
 
 
724
        typeTuple = SearchSysCache(TYPEOID,
 
725
                                                           ObjectIdGetDatum(typoid),
 
726
                                                           0, 0, 0);
 
727
 
 
728
        if (!HeapTupleIsValid(typeTuple))
 
729
        {
 
730
                SPI_result = SPI_ERROR_TYPUNKNOWN;
 
731
                return NULL;
 
732
        }
 
733
 
 
734
        result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
 
735
        ReleaseSysCache(typeTuple);
 
736
        return result;
 
737
}
 
738
 
 
739
Oid
 
740
SPI_gettypeid(TupleDesc tupdesc, int fnumber)
 
741
{
 
742
        SPI_result = 0;
 
743
 
 
744
        if (fnumber > tupdesc->natts || fnumber == 0 ||
 
745
                fnumber <= FirstLowInvalidHeapAttributeNumber)
 
746
        {
 
747
                SPI_result = SPI_ERROR_NOATTRIBUTE;
 
748
                return InvalidOid;
 
749
        }
 
750
 
 
751
        if (fnumber > 0)
 
752
                return tupdesc->attrs[fnumber - 1]->atttypid;
 
753
        else
 
754
                return (SystemAttributeDefinition(fnumber, true))->atttypid;
 
755
}
 
756
 
 
757
char *
 
758
SPI_getrelname(Relation rel)
 
759
{
 
760
        return pstrdup(RelationGetRelationName(rel));
 
761
}
 
762
 
 
763
void *
 
764
SPI_palloc(Size size)
 
765
{
 
766
        MemoryContext oldcxt = NULL;
 
767
        void       *pointer;
 
768
 
 
769
        if (_SPI_curid + 1 == _SPI_connected)           /* connected */
 
770
        {
 
771
                if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
 
772
                        elog(ERROR, "SPI stack corrupted");
 
773
                oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
 
774
        }
 
775
 
 
776
        pointer = palloc(size);
 
777
 
 
778
        if (oldcxt)
 
779
                MemoryContextSwitchTo(oldcxt);
 
780
 
 
781
        return pointer;
 
782
}
 
783
 
 
784
void *
 
785
SPI_repalloc(void *pointer, Size size)
 
786
{
 
787
        /* No longer need to worry which context chunk was in... */
 
788
        return repalloc(pointer, size);
 
789
}
 
790
 
 
791
void
 
792
SPI_pfree(void *pointer)
 
793
{
 
794
        /* No longer need to worry which context chunk was in... */
 
795
        pfree(pointer);
 
796
}
 
797
 
 
798
void
 
799
SPI_freetuple(HeapTuple tuple)
 
800
{
 
801
        /* No longer need to worry which context tuple was in... */
 
802
        heap_freetuple(tuple);
 
803
}
 
804
 
 
805
void
 
806
SPI_freetuptable(SPITupleTable *tuptable)
 
807
{
 
808
        if (tuptable != NULL)
 
809
                MemoryContextDelete(tuptable->tuptabcxt);
 
810
}
 
811
 
 
812
 
 
813
 
 
814
/*
 
815
 * SPI_cursor_open()
 
816
 *
 
817
 *      Open a prepared SPI plan as a portal
 
818
 */
 
819
Portal
 
820
SPI_cursor_open(const char *name, void *plan,
 
821
                                Datum *Values, const char *Nulls,
 
822
                                bool read_only)
 
823
{
 
824
        _SPI_plan  *spiplan = (_SPI_plan *) plan;
 
825
        List       *qtlist = spiplan->qtlist;
 
826
        List       *ptlist = spiplan->ptlist;
 
827
        Query      *queryTree;
 
828
        Plan       *planTree;
 
829
        ParamListInfo paramLI;
 
830
        Snapshot        snapshot;
 
831
        MemoryContext oldcontext;
 
832
        Portal          portal;
 
833
        int                     k;
 
834
 
 
835
        /* Ensure that the plan contains only one query */
 
836
        if (list_length(ptlist) != 1 || list_length(qtlist) != 1)
 
837
                ereport(ERROR,
 
838
                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 
839
                                 errmsg("cannot open multi-query plan as cursor")));
 
840
        queryTree = (Query *) linitial((List *) linitial(qtlist));
 
841
        planTree = (Plan *) linitial(ptlist);
 
842
 
 
843
        /* Must be a query that returns tuples */
 
844
        switch (queryTree->commandType)
 
845
        {
 
846
                case CMD_SELECT:
 
847
                        if (queryTree->into != NULL)
 
848
                                ereport(ERROR,
 
849
                                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 
850
                                                 errmsg("cannot open SELECT INTO query as cursor")));
 
851
                        break;
 
852
                case CMD_UTILITY:
 
853
                        if (!UtilityReturnsTuples(queryTree->utilityStmt))
 
854
                                ereport(ERROR,
 
855
                                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 
856
                                                 errmsg("cannot open non-SELECT query as cursor")));
 
857
                        break;
 
858
                default:
 
859
                        ereport(ERROR,
 
860
                                        (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
 
861
                                         errmsg("cannot open non-SELECT query as cursor")));
 
862
                        break;
 
863
        }
 
864
 
 
865
        /* Reset SPI result */
 
866
        SPI_processed = 0;
 
867
        SPI_tuptable = NULL;
 
868
        _SPI_current->processed = 0;
 
869
        _SPI_current->tuptable = NULL;
 
870
 
 
871
        /* Create the portal */
 
872
        if (name == NULL || name[0] == '\0')
 
873
        {
 
874
                /* Use a random nonconflicting name */
 
875
                portal = CreateNewPortal();
 
876
        }
 
877
        else
 
878
        {
 
879
                /* In this path, error if portal of same name already exists */
 
880
                portal = CreatePortal(name, false, false);
 
881
        }
 
882
 
 
883
        /* Switch to portals memory and copy the parsetree and plan to there */
 
884
        oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
885
        queryTree = copyObject(queryTree);
 
886
        planTree = copyObject(planTree);
 
887
 
 
888
        /* If the plan has parameters, set them up */
 
889
        if (spiplan->nargs > 0)
 
890
        {
 
891
                paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
 
892
                                                                                  sizeof(ParamListInfoData));
 
893
 
 
894
                for (k = 0; k < spiplan->nargs; k++)
 
895
                {
 
896
                        paramLI[k].kind = PARAM_NUM;
 
897
                        paramLI[k].id = k + 1;
 
898
                        paramLI[k].ptype = spiplan->argtypes[k];
 
899
                        paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
 
900
                        if (paramLI[k].isnull)
 
901
                        {
 
902
                                /* nulls just copy */
 
903
                                paramLI[k].value = Values[k];
 
904
                        }
 
905
                        else
 
906
                        {
 
907
                                /* pass-by-ref values must be copied into portal context */
 
908
                                int16           paramTypLen;
 
909
                                bool            paramTypByVal;
 
910
 
 
911
                                get_typlenbyval(spiplan->argtypes[k],
 
912
                                                                &paramTypLen, &paramTypByVal);
 
913
                                paramLI[k].value = datumCopy(Values[k],
 
914
                                                                                         paramTypByVal, paramTypLen);
 
915
                        }
 
916
                }
 
917
                paramLI[k].kind = PARAM_INVALID;
 
918
        }
 
919
        else
 
920
                paramLI = NULL;
 
921
 
 
922
        /*
 
923
         * Set up the portal.
 
924
         */
 
925
        PortalDefineQuery(portal,
 
926
                                          NULL,         /* unfortunately don't have sourceText */
 
927
                                          "SELECT", /* nor the raw parse tree... */
 
928
                                          list_make1(queryTree),
 
929
                                          list_make1(planTree),
 
930
                                          PortalGetHeapMemory(portal));
 
931
 
 
932
        MemoryContextSwitchTo(oldcontext);
 
933
 
 
934
        /*
 
935
         * Set up options for portal.
 
936
         */
 
937
        portal->cursorOptions &= ~(CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL);
 
938
        if (planTree == NULL || ExecSupportsBackwardScan(planTree))
 
939
                portal->cursorOptions |= CURSOR_OPT_SCROLL;
 
940
        else
 
941
                portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
 
942
 
 
943
        /*
 
944
         * Set up the snapshot to use.  (PortalStart will do CopySnapshot,
 
945
         * so we skip that here.)
 
946
         */
 
947
        if (read_only)
 
948
                snapshot = ActiveSnapshot;
 
949
        else
 
950
        {
 
951
                CommandCounterIncrement();
 
952
                snapshot = GetTransactionSnapshot();
 
953
        }
 
954
 
 
955
        /*
 
956
         * Start portal execution.
 
957
         */
 
958
        PortalStart(portal, paramLI, snapshot);
 
959
 
 
960
        Assert(portal->strategy == PORTAL_ONE_SELECT ||
 
961
                   portal->strategy == PORTAL_UTIL_SELECT);
 
962
 
 
963
        /* Return the created portal */
 
964
        return portal;
 
965
}
 
966
 
 
967
 
 
968
/*
 
969
 * SPI_cursor_find()
 
970
 *
 
971
 *      Find the portal of an existing open cursor
 
972
 */
 
973
Portal
 
974
SPI_cursor_find(const char *name)
 
975
{
 
976
        return GetPortalByName(name);
 
977
}
 
978
 
 
979
 
 
980
/*
 
981
 * SPI_cursor_fetch()
 
982
 *
 
983
 *      Fetch rows in a cursor
 
984
 */
 
985
void
 
986
SPI_cursor_fetch(Portal portal, bool forward, int count)
 
987
{
 
988
        _SPI_cursor_operation(portal, forward, count,
 
989
                                                  CreateDestReceiver(SPI, NULL));
 
990
        /* we know that the SPI receiver doesn't need a destroy call */
 
991
}
 
992
 
 
993
 
 
994
/*
 
995
 * SPI_cursor_move()
 
996
 *
 
997
 *      Move in a cursor
 
998
 */
 
999
void
 
1000
SPI_cursor_move(Portal portal, bool forward, int count)
 
1001
{
 
1002
        _SPI_cursor_operation(portal, forward, count, None_Receiver);
 
1003
}
 
1004
 
 
1005
 
 
1006
/*
 
1007
 * SPI_cursor_close()
 
1008
 *
 
1009
 *      Close a cursor
 
1010
 */
 
1011
void
 
1012
SPI_cursor_close(Portal portal)
 
1013
{
 
1014
        if (!PortalIsValid(portal))
 
1015
                elog(ERROR, "invalid portal in SPI cursor operation");
 
1016
 
 
1017
        PortalDrop(portal, false);
 
1018
}
 
1019
 
 
1020
/*
 
1021
 * Returns the Oid representing the type id for argument at argIndex. First
 
1022
 * parameter is at index zero.
 
1023
 */
 
1024
Oid
 
1025
SPI_getargtypeid(void *plan, int argIndex)
 
1026
{
 
1027
        if (plan == NULL || argIndex < 0 || argIndex >= ((_SPI_plan *) plan)->nargs)
 
1028
        {
 
1029
                SPI_result = SPI_ERROR_ARGUMENT;
 
1030
                return InvalidOid;
 
1031
        }
 
1032
        return ((_SPI_plan *) plan)->argtypes[argIndex];
 
1033
}
 
1034
 
 
1035
/*
 
1036
 * Returns the number of arguments for the prepared plan.
 
1037
 */
 
1038
int
 
1039
SPI_getargcount(void *plan)
 
1040
{
 
1041
        if (plan == NULL)
 
1042
        {
 
1043
                SPI_result = SPI_ERROR_ARGUMENT;
 
1044
                return -1;
 
1045
        }
 
1046
        return ((_SPI_plan *) plan)->nargs;
 
1047
}
 
1048
 
 
1049
/*
 
1050
 * Returns true if the plan contains exactly one command
 
1051
 * and that command originates from normal SELECT (i.e.
 
1052
 * *not* a SELECT ... INTO). In essence, the result indicates
 
1053
 * if the command can be used with SPI_cursor_open
 
1054
 *
 
1055
 * Parameters
 
1056
 *        plan A plan previously prepared using SPI_prepare
 
1057
 */
 
1058
bool
 
1059
SPI_is_cursor_plan(void *plan)
 
1060
{
 
1061
        _SPI_plan  *spiplan = (_SPI_plan *) plan;
 
1062
        List       *qtlist;
 
1063
 
 
1064
        if (spiplan == NULL)
 
1065
        {
 
1066
                SPI_result = SPI_ERROR_ARGUMENT;
 
1067
                return false;
 
1068
        }
 
1069
 
 
1070
        qtlist = spiplan->qtlist;
 
1071
        if (list_length(spiplan->ptlist) == 1 && list_length(qtlist) == 1)
 
1072
        {
 
1073
                Query      *queryTree = (Query *) linitial((List *) linitial(qtlist));
 
1074
 
 
1075
                if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
 
1076
                        return true;
 
1077
        }
 
1078
        return false;
 
1079
}
 
1080
 
 
1081
/*
 
1082
 * SPI_result_code_string --- convert any SPI return code to a string
 
1083
 *
 
1084
 * This is often useful in error messages.      Most callers will probably
 
1085
 * only pass negative (error-case) codes, but for generality we recognize
 
1086
 * the success codes too.
 
1087
 */
 
1088
const char *
 
1089
SPI_result_code_string(int code)
 
1090
{
 
1091
        static char buf[64];
 
1092
 
 
1093
        switch (code)
 
1094
        {
 
1095
                case SPI_ERROR_CONNECT:
 
1096
                        return "SPI_ERROR_CONNECT";
 
1097
                case SPI_ERROR_COPY:
 
1098
                        return "SPI_ERROR_COPY";
 
1099
                case SPI_ERROR_OPUNKNOWN:
 
1100
                        return "SPI_ERROR_OPUNKNOWN";
 
1101
                case SPI_ERROR_UNCONNECTED:
 
1102
                        return "SPI_ERROR_UNCONNECTED";
 
1103
                case SPI_ERROR_CURSOR:
 
1104
                        return "SPI_ERROR_CURSOR";
 
1105
                case SPI_ERROR_ARGUMENT:
 
1106
                        return "SPI_ERROR_ARGUMENT";
 
1107
                case SPI_ERROR_PARAM:
 
1108
                        return "SPI_ERROR_PARAM";
 
1109
                case SPI_ERROR_TRANSACTION:
 
1110
                        return "SPI_ERROR_TRANSACTION";
 
1111
                case SPI_ERROR_NOATTRIBUTE:
 
1112
                        return "SPI_ERROR_NOATTRIBUTE";
 
1113
                case SPI_ERROR_NOOUTFUNC:
 
1114
                        return "SPI_ERROR_NOOUTFUNC";
 
1115
                case SPI_ERROR_TYPUNKNOWN:
 
1116
                        return "SPI_ERROR_TYPUNKNOWN";
 
1117
                case SPI_OK_CONNECT:
 
1118
                        return "SPI_OK_CONNECT";
 
1119
                case SPI_OK_FINISH:
 
1120
                        return "SPI_OK_FINISH";
 
1121
                case SPI_OK_FETCH:
 
1122
                        return "SPI_OK_FETCH";
 
1123
                case SPI_OK_UTILITY:
 
1124
                        return "SPI_OK_UTILITY";
 
1125
                case SPI_OK_SELECT:
 
1126
                        return "SPI_OK_SELECT";
 
1127
                case SPI_OK_SELINTO:
 
1128
                        return "SPI_OK_SELINTO";
 
1129
                case SPI_OK_INSERT:
 
1130
                        return "SPI_OK_INSERT";
 
1131
                case SPI_OK_DELETE:
 
1132
                        return "SPI_OK_DELETE";
 
1133
                case SPI_OK_UPDATE:
 
1134
                        return "SPI_OK_UPDATE";
 
1135
                case SPI_OK_CURSOR:
 
1136
                        return "SPI_OK_CURSOR";
 
1137
        }
 
1138
        /* Unrecognized code ... return something useful ... */
 
1139
        sprintf(buf, "Unrecognized SPI code %d", code);
 
1140
        return buf;
 
1141
}
 
1142
 
 
1143
/* =================== private functions =================== */
 
1144
 
 
1145
/*
 
1146
 * spi_dest_startup
 
1147
 *              Initialize to receive tuples from Executor into SPITupleTable
 
1148
 *              of current SPI procedure
 
1149
 */
 
1150
void
 
1151
spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 
1152
{
 
1153
        SPITupleTable *tuptable;
 
1154
        MemoryContext oldcxt;
 
1155
        MemoryContext tuptabcxt;
 
1156
 
 
1157
        /*
 
1158
         * When called by Executor _SPI_curid expected to be equal to
 
1159
         * _SPI_connected
 
1160
         */
 
1161
        if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
 
1162
                elog(ERROR, "improper call to spi_dest_startup");
 
1163
        if (_SPI_current != &(_SPI_stack[_SPI_curid]))
 
1164
                elog(ERROR, "SPI stack corrupted");
 
1165
 
 
1166
        if (_SPI_current->tuptable != NULL)
 
1167
                elog(ERROR, "improper call to spi_dest_startup");
 
1168
 
 
1169
        oldcxt = _SPI_procmem();        /* switch to procedure memory context */
 
1170
 
 
1171
        tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
 
1172
                                                                          "SPI TupTable",
 
1173
                                                                          ALLOCSET_DEFAULT_MINSIZE,
 
1174
                                                                          ALLOCSET_DEFAULT_INITSIZE,
 
1175
                                                                          ALLOCSET_DEFAULT_MAXSIZE);
 
1176
        MemoryContextSwitchTo(tuptabcxt);
 
1177
 
 
1178
        _SPI_current->tuptable = tuptable = (SPITupleTable *)
 
1179
                palloc(sizeof(SPITupleTable));
 
1180
        tuptable->tuptabcxt = tuptabcxt;
 
1181
        tuptable->alloced = tuptable->free = 128;
 
1182
        tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
 
1183
        tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
 
1184
 
 
1185
        MemoryContextSwitchTo(oldcxt);
 
1186
}
 
1187
 
 
1188
/*
 
1189
 * spi_printtup
 
1190
 *              store tuple retrieved by Executor into SPITupleTable
 
1191
 *              of current SPI procedure
 
1192
 */
 
1193
void
 
1194
spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
 
1195
{
 
1196
        SPITupleTable *tuptable;
 
1197
        MemoryContext oldcxt;
 
1198
 
 
1199
        /*
 
1200
         * When called by Executor _SPI_curid expected to be equal to
 
1201
         * _SPI_connected
 
1202
         */
 
1203
        if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
 
1204
                elog(ERROR, "improper call to spi_printtup");
 
1205
        if (_SPI_current != &(_SPI_stack[_SPI_curid]))
 
1206
                elog(ERROR, "SPI stack corrupted");
 
1207
 
 
1208
        tuptable = _SPI_current->tuptable;
 
1209
        if (tuptable == NULL)
 
1210
                elog(ERROR, "improper call to spi_printtup");
 
1211
 
 
1212
        oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
 
1213
 
 
1214
        if (tuptable->free == 0)
 
1215
        {
 
1216
                tuptable->free = 256;
 
1217
                tuptable->alloced += tuptable->free;
 
1218
                tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
 
1219
                                                                  tuptable->alloced * sizeof(HeapTuple));
 
1220
        }
 
1221
 
 
1222
        tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
 
1223
        (tuptable->free)--;
 
1224
 
 
1225
        MemoryContextSwitchTo(oldcxt);
 
1226
}
 
1227
 
 
1228
/*
 
1229
 * Static functions
 
1230
 */
 
1231
 
 
1232
/*
 
1233
 * Parse and plan a querystring.
 
1234
 *
 
1235
 * At entry, plan->argtypes and plan->nargs must be valid.
 
1236
 *
 
1237
 * Query and plan lists are stored into *plan.
 
1238
 */
 
1239
static void
 
1240
_SPI_prepare_plan(const char *src, _SPI_plan *plan)
 
1241
{
 
1242
        List       *raw_parsetree_list;
 
1243
        List       *query_list_list;
 
1244
        List       *plan_list;
 
1245
        ListCell   *list_item;
 
1246
        ErrorContextCallback spierrcontext;
 
1247
        Oid                *argtypes = plan->argtypes;
 
1248
        int                     nargs = plan->nargs;
 
1249
 
 
1250
        /*
 
1251
         * Increment CommandCounter to see changes made by now.  We must do
 
1252
         * this to be sure of seeing any schema changes made by a just-preceding
 
1253
         * SPI command.  (But we don't bother advancing the snapshot, since the
 
1254
         * planner generally operates under SnapshotNow rules anyway.)
 
1255
         */
 
1256
        CommandCounterIncrement();
 
1257
 
 
1258
        /*
 
1259
         * Setup error traceback support for ereport()
 
1260
         */
 
1261
        spierrcontext.callback = _SPI_error_callback;
 
1262
        spierrcontext.arg = (void *) src;
 
1263
        spierrcontext.previous = error_context_stack;
 
1264
        error_context_stack = &spierrcontext;
 
1265
 
 
1266
        /*
 
1267
         * Parse the request string into a list of raw parse trees.
 
1268
         */
 
1269
        raw_parsetree_list = pg_parse_query(src);
 
1270
 
 
1271
        /*
 
1272
         * Do parse analysis and rule rewrite for each raw parsetree.
 
1273
         *
 
1274
         * We save the querytrees from each raw parsetree as a separate
 
1275
         * sublist.  This allows _SPI_execute_plan() to know where the
 
1276
         * boundaries between original queries fall.
 
1277
         */
 
1278
        query_list_list = NIL;
 
1279
        plan_list = NIL;
 
1280
 
 
1281
        foreach(list_item, raw_parsetree_list)
 
1282
        {
 
1283
                Node       *parsetree = (Node *) lfirst(list_item);
 
1284
                List       *query_list;
 
1285
 
 
1286
                query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
 
1287
 
 
1288
                query_list_list = lappend(query_list_list, query_list);
 
1289
 
 
1290
                plan_list = list_concat(plan_list,
 
1291
                                                                pg_plan_queries(query_list, NULL, false));
 
1292
        }
 
1293
 
 
1294
        plan->qtlist = query_list_list;
 
1295
        plan->ptlist = plan_list;
 
1296
 
 
1297
        /*
 
1298
         * Pop the error context stack
 
1299
         */
 
1300
        error_context_stack = spierrcontext.previous;
 
1301
}
 
1302
 
 
1303
/*
 
1304
 * Execute the given plan with the given parameter values
 
1305
 *
 
1306
 * snapshot: query snapshot to use, or InvalidSnapshot for the normal
 
1307
 *              behavior of taking a new snapshot for each query.
 
1308
 * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
 
1309
 * read_only: TRUE for read-only execution (no CommandCounterIncrement)
 
1310
 * tcount: execution tuple-count limit, or 0 for none
 
1311
 */
 
1312
static int
 
1313
_SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
 
1314
                                  Snapshot snapshot, Snapshot crosscheck_snapshot,
 
1315
                                  bool read_only, int tcount)
 
1316
{
 
1317
        volatile int res = 0;
 
1318
        Snapshot        saveActiveSnapshot;
 
1319
 
 
1320
        /* Be sure to restore ActiveSnapshot on error exit */
 
1321
        saveActiveSnapshot = ActiveSnapshot;
 
1322
        PG_TRY();
 
1323
        {
 
1324
                List       *query_list_list = plan->qtlist;
 
1325
                ListCell   *plan_list_item = list_head(plan->ptlist);
 
1326
                ListCell   *query_list_list_item;
 
1327
                ErrorContextCallback spierrcontext;
 
1328
                int                     nargs = plan->nargs;
 
1329
                ParamListInfo paramLI;
 
1330
 
 
1331
                /* Convert parameters to form wanted by executor */
 
1332
                if (nargs > 0)
 
1333
                {
 
1334
                        int                     k;
 
1335
 
 
1336
                        paramLI = (ParamListInfo)
 
1337
                                palloc0((nargs + 1) * sizeof(ParamListInfoData));
 
1338
 
 
1339
                        for (k = 0; k < nargs; k++)
 
1340
                        {
 
1341
                                paramLI[k].kind = PARAM_NUM;
 
1342
                                paramLI[k].id = k + 1;
 
1343
                                paramLI[k].ptype = plan->argtypes[k];
 
1344
                                paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
 
1345
                                paramLI[k].value = Values[k];
 
1346
                        }
 
1347
                        paramLI[k].kind = PARAM_INVALID;
 
1348
                }
 
1349
                else
 
1350
                        paramLI = NULL;
 
1351
 
 
1352
                /* Reset state (only needed in case string is empty) */
 
1353
                SPI_processed = 0;
 
1354
                SPI_lastoid = InvalidOid;
 
1355
                SPI_tuptable = NULL;
 
1356
                _SPI_current->tuptable = NULL;
 
1357
 
 
1358
                /*
 
1359
                 * Setup error traceback support for ereport()
 
1360
                 */
 
1361
                spierrcontext.callback = _SPI_error_callback;
 
1362
                spierrcontext.arg = (void *) plan->query;
 
1363
                spierrcontext.previous = error_context_stack;
 
1364
                error_context_stack = &spierrcontext;
 
1365
 
 
1366
                foreach(query_list_list_item, query_list_list)
 
1367
                {
 
1368
                        List       *query_list = lfirst(query_list_list_item);
 
1369
                        ListCell   *query_list_item;
 
1370
 
 
1371
                        /* Reset state for each original parsetree */
 
1372
                        /* (at most one of its querytrees will be marked canSetTag) */
 
1373
                        SPI_processed = 0;
 
1374
                        SPI_lastoid = InvalidOid;
 
1375
                        SPI_tuptable = NULL;
 
1376
                        _SPI_current->tuptable = NULL;
 
1377
 
 
1378
                        foreach(query_list_item, query_list)
 
1379
                        {
 
1380
                                Query      *queryTree = (Query *) lfirst(query_list_item);
 
1381
                                Plan       *planTree;
 
1382
                                QueryDesc  *qdesc;
 
1383
                                DestReceiver *dest;
 
1384
 
 
1385
                                planTree = lfirst(plan_list_item);
 
1386
                                plan_list_item = lnext(plan_list_item);
 
1387
 
 
1388
                                if (queryTree->commandType == CMD_UTILITY)
 
1389
                                {
 
1390
                                        if (IsA(queryTree->utilityStmt, CopyStmt))
 
1391
                                        {
 
1392
                                                CopyStmt   *stmt = (CopyStmt *) queryTree->utilityStmt;
 
1393
 
 
1394
                                                if (stmt->filename == NULL)
 
1395
                                                {
 
1396
                                                        res = SPI_ERROR_COPY;
 
1397
                                                        goto fail;
 
1398
                                                }
 
1399
                                        }
 
1400
                                        else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
 
1401
                                                         IsA(queryTree->utilityStmt, ClosePortalStmt) ||
 
1402
                                                         IsA(queryTree->utilityStmt, FetchStmt))
 
1403
                                        {
 
1404
                                                res = SPI_ERROR_CURSOR;
 
1405
                                                goto fail;
 
1406
                                        }
 
1407
                                        else if (IsA(queryTree->utilityStmt, TransactionStmt))
 
1408
                                        {
 
1409
                                                res = SPI_ERROR_TRANSACTION;
 
1410
                                                goto fail;
 
1411
                                        }
 
1412
                                }
 
1413
 
 
1414
                                if (read_only && !QueryIsReadOnly(queryTree))
 
1415
                                        ereport(ERROR,
 
1416
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1417
                                                         /* translator: %s is a SQL statement name */
 
1418
                                                         errmsg("%s is not allowed in a non-volatile function",
 
1419
                                                                        CreateQueryTag(queryTree))));
 
1420
                                /*
 
1421
                                 * If not read-only mode, advance the command counter before
 
1422
                                 * each command.
 
1423
                                 */
 
1424
                                if (!read_only)
 
1425
                                        CommandCounterIncrement();
 
1426
 
 
1427
                                dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None,
 
1428
                                                                                  NULL);
 
1429
 
 
1430
                                if (snapshot == InvalidSnapshot)
 
1431
                                {
 
1432
                                        /*
 
1433
                                         * Default read_only behavior is to use the entry-time
 
1434
                                         * ActiveSnapshot; if read-write, grab a full new snap.
 
1435
                                         */
 
1436
                                        if (read_only)
 
1437
                                                ActiveSnapshot = CopySnapshot(saveActiveSnapshot);
 
1438
                                        else
 
1439
                                                ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
 
1440
                                }
 
1441
                                else
 
1442
                                {
 
1443
                                        /*
 
1444
                                         * We interpret read_only with a specified snapshot to be
 
1445
                                         * exactly that snapshot, but read-write means use the
 
1446
                                         * snap with advancing of command ID.
 
1447
                                         */
 
1448
                                        ActiveSnapshot = CopySnapshot(snapshot);
 
1449
                                        if (!read_only)
 
1450
                                                ActiveSnapshot->curcid = GetCurrentCommandId();
 
1451
                                }
 
1452
 
 
1453
                                if (queryTree->commandType == CMD_UTILITY)
 
1454
                                {
 
1455
                                        ProcessUtility(queryTree->utilityStmt, paramLI,
 
1456
                                                                   dest, NULL);
 
1457
                                        res = SPI_OK_UTILITY;
 
1458
                                }
 
1459
                                else
 
1460
                                {
 
1461
                                        qdesc = CreateQueryDesc(queryTree, planTree,
 
1462
                                                                                        ActiveSnapshot,
 
1463
                                                                                        crosscheck_snapshot,
 
1464
                                                                                        dest,
 
1465
                                                                                        paramLI, false);
 
1466
                                        res = _SPI_pquery(qdesc,
 
1467
                                                                          queryTree->canSetTag ? tcount : 0);
 
1468
                                        FreeQueryDesc(qdesc);
 
1469
                                }
 
1470
                                FreeSnapshot(ActiveSnapshot);
 
1471
                                ActiveSnapshot = NULL;
 
1472
                                /* we know that the receiver doesn't need a destroy call */
 
1473
                                if (res < 0)
 
1474
                                        goto fail;
 
1475
                        }
 
1476
                }
 
1477
 
 
1478
fail:
 
1479
 
 
1480
                /*
 
1481
                 * Pop the error context stack
 
1482
                 */
 
1483
                error_context_stack = spierrcontext.previous;
 
1484
        }
 
1485
        PG_CATCH();
 
1486
        {
 
1487
                /* Restore global vars and propagate error */
 
1488
                ActiveSnapshot = saveActiveSnapshot;
 
1489
                PG_RE_THROW();
 
1490
        }
 
1491
        PG_END_TRY();
 
1492
 
 
1493
        ActiveSnapshot = saveActiveSnapshot;
 
1494
 
 
1495
        return res;
 
1496
}
 
1497
 
 
1498
static int
 
1499
_SPI_pquery(QueryDesc *queryDesc, int tcount)
 
1500
{
 
1501
        int                     operation = queryDesc->operation;
 
1502
        int                     res;
 
1503
        Oid                     save_lastoid;
 
1504
 
 
1505
        switch (operation)
 
1506
        {
 
1507
                case CMD_SELECT:
 
1508
                        res = SPI_OK_SELECT;
 
1509
                        if (queryDesc->parsetree->into != NULL)         /* select into table */
 
1510
                        {
 
1511
                                res = SPI_OK_SELINTO;
 
1512
                                queryDesc->dest = None_Receiver;                /* don't output results */
 
1513
                        }
 
1514
                        break;
 
1515
                case CMD_INSERT:
 
1516
                        res = SPI_OK_INSERT;
 
1517
                        break;
 
1518
                case CMD_DELETE:
 
1519
                        res = SPI_OK_DELETE;
 
1520
                        break;
 
1521
                case CMD_UPDATE:
 
1522
                        res = SPI_OK_UPDATE;
 
1523
                        break;
 
1524
                default:
 
1525
                        return SPI_ERROR_OPUNKNOWN;
 
1526
        }
 
1527
 
 
1528
#ifdef SPI_EXECUTOR_STATS
 
1529
        if (ShowExecutorStats)
 
1530
                ResetUsage();
 
1531
#endif
 
1532
 
 
1533
        AfterTriggerBeginQuery();
 
1534
 
 
1535
        ExecutorStart(queryDesc, false);
 
1536
 
 
1537
        ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
 
1538
 
 
1539
        _SPI_current->processed = queryDesc->estate->es_processed;
 
1540
        save_lastoid = queryDesc->estate->es_lastoid;
 
1541
 
 
1542
        if (operation == CMD_SELECT && queryDesc->dest->mydest == SPI)
 
1543
        {
 
1544
                if (_SPI_checktuples())
 
1545
                        elog(ERROR, "consistency check on SPI tuple count failed");
 
1546
        }
 
1547
 
 
1548
        ExecutorEnd(queryDesc);
 
1549
 
 
1550
        /* Take care of any queued AFTER triggers */
 
1551
        AfterTriggerEndQuery();
 
1552
 
 
1553
        if (queryDesc->dest->mydest == SPI)
 
1554
        {
 
1555
                SPI_processed = _SPI_current->processed;
 
1556
                SPI_lastoid = save_lastoid;
 
1557
                SPI_tuptable = _SPI_current->tuptable;
 
1558
        }
 
1559
        else if (res == SPI_OK_SELECT)
 
1560
        {
 
1561
                /* Don't return SPI_OK_SELECT if we discarded the result */
 
1562
                res = SPI_OK_UTILITY;
 
1563
        }
 
1564
 
 
1565
#ifdef SPI_EXECUTOR_STATS
 
1566
        if (ShowExecutorStats)
 
1567
                ShowUsage("SPI EXECUTOR STATS");
 
1568
#endif
 
1569
 
 
1570
        return res;
 
1571
}
 
1572
 
 
1573
/*
 
1574
 * _SPI_error_callback
 
1575
 *
 
1576
 * Add context information when a query invoked via SPI fails
 
1577
 */
 
1578
static void
 
1579
_SPI_error_callback(void *arg)
 
1580
{
 
1581
        const char *query = (const char *) arg;
 
1582
        int                     syntaxerrposition;
 
1583
 
 
1584
        /*
 
1585
         * If there is a syntax error position, convert to internal syntax
 
1586
         * error; otherwise treat the query as an item of context stack
 
1587
         */
 
1588
        syntaxerrposition = geterrposition();
 
1589
        if (syntaxerrposition > 0)
 
1590
        {
 
1591
                errposition(0);
 
1592
                internalerrposition(syntaxerrposition);
 
1593
                internalerrquery(query);
 
1594
        }
 
1595
        else
 
1596
                errcontext("SQL statement \"%s\"", query);
 
1597
}
 
1598
 
 
1599
/*
 
1600
 * _SPI_cursor_operation()
 
1601
 *
 
1602
 *      Do a FETCH or MOVE in a cursor
 
1603
 */
 
1604
static void
 
1605
_SPI_cursor_operation(Portal portal, bool forward, int count,
 
1606
                                          DestReceiver *dest)
 
1607
{
 
1608
        long            nfetched;
 
1609
 
 
1610
        /* Check that the portal is valid */
 
1611
        if (!PortalIsValid(portal))
 
1612
                elog(ERROR, "invalid portal in SPI cursor operation");
 
1613
 
 
1614
        /* Push the SPI stack */
 
1615
        if (_SPI_begin_call(true) < 0)
 
1616
                elog(ERROR, "SPI cursor operation called while not connected");
 
1617
 
 
1618
        /* Reset the SPI result */
 
1619
        SPI_processed = 0;
 
1620
        SPI_tuptable = NULL;
 
1621
        _SPI_current->processed = 0;
 
1622
        _SPI_current->tuptable = NULL;
 
1623
 
 
1624
        /* Run the cursor */
 
1625
        nfetched = PortalRunFetch(portal,
 
1626
                                                          forward ? FETCH_FORWARD : FETCH_BACKWARD,
 
1627
                                                          (long) count,
 
1628
                                                          dest);
 
1629
 
 
1630
        /*
 
1631
         * Think not to combine this store with the preceding function call.
 
1632
         * If the portal contains calls to functions that use SPI, then
 
1633
         * SPI_stack is likely to move around while the portal runs.  When
 
1634
         * control returns, _SPI_current will point to the correct stack
 
1635
         * entry... but the pointer may be different than it was beforehand.
 
1636
         * So we must be sure to re-fetch the pointer after the function call
 
1637
         * completes.
 
1638
         */
 
1639
        _SPI_current->processed = nfetched;
 
1640
 
 
1641
        if (dest->mydest == SPI && _SPI_checktuples())
 
1642
                elog(ERROR, "consistency check on SPI tuple count failed");
 
1643
 
 
1644
        /* Put the result into place for access by caller */
 
1645
        SPI_processed = _SPI_current->processed;
 
1646
        SPI_tuptable = _SPI_current->tuptable;
 
1647
 
 
1648
        /* Pop the SPI stack */
 
1649
        _SPI_end_call(true);
 
1650
}
 
1651
 
 
1652
 
 
1653
static MemoryContext
 
1654
_SPI_execmem(void)
 
1655
{
 
1656
        return MemoryContextSwitchTo(_SPI_current->execCxt);
 
1657
}
 
1658
 
 
1659
static MemoryContext
 
1660
_SPI_procmem(void)
 
1661
{
 
1662
        return MemoryContextSwitchTo(_SPI_current->procCxt);
 
1663
}
 
1664
 
 
1665
/*
 
1666
 * _SPI_begin_call: begin a SPI operation within a connected procedure
 
1667
 */
 
1668
static int
 
1669
_SPI_begin_call(bool execmem)
 
1670
{
 
1671
        if (_SPI_curid + 1 != _SPI_connected)
 
1672
                return SPI_ERROR_UNCONNECTED;
 
1673
        _SPI_curid++;
 
1674
        if (_SPI_current != &(_SPI_stack[_SPI_curid]))
 
1675
                elog(ERROR, "SPI stack corrupted");
 
1676
 
 
1677
        if (execmem)                            /* switch to the Executor memory context */
 
1678
                _SPI_execmem();
 
1679
 
 
1680
        return 0;
 
1681
}
 
1682
 
 
1683
/*
 
1684
 * _SPI_end_call: end a SPI operation within a connected procedure
 
1685
 *
 
1686
 * Note: this currently has no failure return cases, so callers don't check
 
1687
 */
 
1688
static int
 
1689
_SPI_end_call(bool procmem)
 
1690
{
 
1691
        /*
 
1692
         * We're returning to procedure where _SPI_curid == _SPI_connected - 1
 
1693
         */
 
1694
        _SPI_curid--;
 
1695
 
 
1696
        if (procmem)                            /* switch to the procedure memory context */
 
1697
        {
 
1698
                _SPI_procmem();
 
1699
                /* and free Executor memory */
 
1700
                MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
 
1701
        }
 
1702
 
 
1703
        return 0;
 
1704
}
 
1705
 
 
1706
static bool
 
1707
_SPI_checktuples(void)
 
1708
{
 
1709
        uint32          processed = _SPI_current->processed;
 
1710
        SPITupleTable *tuptable = _SPI_current->tuptable;
 
1711
        bool            failed = false;
 
1712
 
 
1713
        if (tuptable == NULL)           /* spi_dest_startup was not called */
 
1714
                failed = true;
 
1715
        else if (processed != (tuptable->alloced - tuptable->free))
 
1716
                failed = true;
 
1717
 
 
1718
        return failed;
 
1719
}
 
1720
 
 
1721
static _SPI_plan *
 
1722
_SPI_copy_plan(_SPI_plan *plan, int location)
 
1723
{
 
1724
        _SPI_plan  *newplan;
 
1725
        MemoryContext oldcxt;
 
1726
        MemoryContext plancxt;
 
1727
        MemoryContext parentcxt;
 
1728
 
 
1729
        /* Determine correct parent for the plan's memory context */
 
1730
        if (location == _SPI_CPLAN_PROCXT)
 
1731
                parentcxt = _SPI_current->procCxt;
 
1732
        else if (location == _SPI_CPLAN_TOPCXT)
 
1733
                parentcxt = TopMemoryContext;
 
1734
        else    /* (this case not currently used) */
 
1735
                parentcxt = CurrentMemoryContext;
 
1736
 
 
1737
        /*
 
1738
         * Create a memory context for the plan.  We don't expect the plan to
 
1739
         * be very large, so use smaller-than-default alloc parameters.
 
1740
         */
 
1741
        plancxt = AllocSetContextCreate(parentcxt,
 
1742
                                                                        "SPI Plan",
 
1743
                                                                        ALLOCSET_SMALL_MINSIZE,
 
1744
                                                                        ALLOCSET_SMALL_INITSIZE,
 
1745
                                                                        ALLOCSET_SMALL_MAXSIZE);
 
1746
        oldcxt = MemoryContextSwitchTo(plancxt);
 
1747
 
 
1748
        /* Copy the SPI plan into its own context */
 
1749
        newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
 
1750
        newplan->plancxt = plancxt;
 
1751
        newplan->query = pstrdup(plan->query);
 
1752
        newplan->qtlist = (List *) copyObject(plan->qtlist);
 
1753
        newplan->ptlist = (List *) copyObject(plan->ptlist);
 
1754
        newplan->nargs = plan->nargs;
 
1755
        if (plan->nargs > 0)
 
1756
        {
 
1757
                newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
 
1758
                memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
 
1759
        }
 
1760
        else
 
1761
                newplan->argtypes = NULL;
 
1762
 
 
1763
        MemoryContextSwitchTo(oldcxt);
 
1764
 
 
1765
        return newplan;
 
1766
}