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