1 /*-------------------------------------------------------------------------
2 *
3 * spi.c
4 * Server Programming Interface
5 *
6 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/executor/spi.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16
17 #include "access/htup_details.h"
18 #include "access/printtup.h"
19 #include "access/sysattr.h"
20 #include "access/xact.h"
21 #include "catalog/heap.h"
22 #include "catalog/pg_type.h"
23 #include "commands/trigger.h"
24 #include "executor/executor.h"
25 #include "executor/spi_priv.h"
26 #include "miscadmin.h"
27 #include "tcop/pquery.h"
28 #include "tcop/utility.h"
29 #include "utils/builtins.h"
30 #include "utils/datum.h"
31 #include "utils/lsyscache.h"
32 #include "utils/memutils.h"
33 #include "utils/rel.h"
34 #include "utils/snapmgr.h"
35 #include "utils/syscache.h"
36 #include "utils/typcache.h"
37
38
39 /*
40 * These global variables are part of the API for various SPI functions
41 * (a horrible API choice, but it's too late now). To reduce the risk of
42 * interference between different SPI callers, we save and restore them
43 * when entering/exiting a SPI nesting level.
44 */
45 uint64 SPI_processed = 0;
46 Oid SPI_lastoid = InvalidOid;
47 SPITupleTable *SPI_tuptable = NULL;
48 int SPI_result = 0;
49
50 static _SPI_connection *_SPI_stack = NULL;
51 static _SPI_connection *_SPI_current = NULL;
52 static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
53 static int _SPI_connected = -1; /* current stack index */
54
55 static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
56 ParamListInfo paramLI, bool read_only);
57
58 static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
59
60 static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
61
62 static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
63 Snapshot snapshot, Snapshot crosscheck_snapshot,
64 bool read_only, bool fire_triggers, uint64 tcount);
65
66 static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
67 Datum *Values, const char *Nulls);
68
69 static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount);
70
71 static void _SPI_error_callback(void *arg);
72
73 static void _SPI_cursor_operation(Portal portal,
74 FetchDirection direction, long count,
75 DestReceiver *dest);
76
77 static SPIPlanPtr _SPI_make_plan_non_temp(SPIPlanPtr plan);
78 static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
79
80 static int _SPI_begin_call(bool use_exec);
81 static int _SPI_end_call(bool use_exec);
82 static MemoryContext _SPI_execmem(void);
83 static MemoryContext _SPI_procmem(void);
84 static bool _SPI_checktuples(void);
85
86
87 /* =================== interface functions =================== */
88
89 int
SPI_connect(void)90 SPI_connect(void)
91 {
92 int newdepth;
93
94 /* Enlarge stack if necessary */
95 if (_SPI_stack == NULL)
96 {
97 if (_SPI_connected != -1 || _SPI_stack_depth != 0)
98 elog(ERROR, "SPI stack corrupted");
99 newdepth = 16;
100 _SPI_stack = (_SPI_connection *)
101 MemoryContextAlloc(TopTransactionContext,
102 newdepth * sizeof(_SPI_connection));
103 _SPI_stack_depth = newdepth;
104 }
105 else
106 {
107 if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
108 elog(ERROR, "SPI stack corrupted");
109 if (_SPI_stack_depth == _SPI_connected + 1)
110 {
111 newdepth = _SPI_stack_depth * 2;
112 _SPI_stack = (_SPI_connection *)
113 repalloc(_SPI_stack,
114 newdepth * sizeof(_SPI_connection));
115 _SPI_stack_depth = newdepth;
116 }
117 }
118
119 /* Enter new stack level */
120 _SPI_connected++;
121 Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
122
123 _SPI_current = &(_SPI_stack[_SPI_connected]);
124 _SPI_current->processed = 0;
125 _SPI_current->lastoid = InvalidOid;
126 _SPI_current->tuptable = NULL;
127 _SPI_current->execSubid = InvalidSubTransactionId;
128 slist_init(&_SPI_current->tuptables);
129 _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
130 _SPI_current->execCxt = NULL;
131 _SPI_current->connectSubid = GetCurrentSubTransactionId();
132 _SPI_current->queryEnv = NULL;
133 _SPI_current->outer_processed = SPI_processed;
134 _SPI_current->outer_lastoid = SPI_lastoid;
135 _SPI_current->outer_tuptable = SPI_tuptable;
136 _SPI_current->outer_result = SPI_result;
137
138 /*
139 * Create memory contexts for this procedure
140 *
141 * XXX it would be better to use PortalContext as the parent context, but
142 * we may not be inside a portal (consider deferred-trigger execution).
143 * Perhaps CurTransactionContext would do? For now it doesn't matter
144 * because we clean up explicitly in AtEOSubXact_SPI().
145 */
146 _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
147 "SPI Proc",
148 ALLOCSET_DEFAULT_SIZES);
149 _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
150 "SPI Exec",
151 ALLOCSET_DEFAULT_SIZES);
152 /* ... and switch to procedure's context */
153 _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
154
155 /*
156 * Reset API global variables so that current caller cannot accidentally
157 * depend on state of an outer caller.
158 */
159 SPI_processed = 0;
160 SPI_lastoid = InvalidOid;
161 SPI_tuptable = NULL;
162 SPI_result = 0;
163
164 return SPI_OK_CONNECT;
165 }
166
167 int
SPI_finish(void)168 SPI_finish(void)
169 {
170 int res;
171
172 res = _SPI_begin_call(false); /* just check we're connected */
173 if (res < 0)
174 return res;
175
176 /* Restore memory context as it was before procedure call */
177 MemoryContextSwitchTo(_SPI_current->savedcxt);
178
179 /* Release memory used in procedure call (including tuptables) */
180 MemoryContextDelete(_SPI_current->execCxt);
181 _SPI_current->execCxt = NULL;
182 MemoryContextDelete(_SPI_current->procCxt);
183 _SPI_current->procCxt = NULL;
184
185 /*
186 * Restore outer API variables, especially SPI_tuptable which is probably
187 * pointing at a just-deleted tuptable
188 */
189 SPI_processed = _SPI_current->outer_processed;
190 SPI_lastoid = _SPI_current->outer_lastoid;
191 SPI_tuptable = _SPI_current->outer_tuptable;
192 SPI_result = _SPI_current->outer_result;
193
194 /* Exit stack level */
195 _SPI_connected--;
196 if (_SPI_connected < 0)
197 _SPI_current = NULL;
198 else
199 _SPI_current = &(_SPI_stack[_SPI_connected]);
200
201 return SPI_OK_FINISH;
202 }
203
204 /*
205 * Clean up SPI state at transaction commit or abort.
206 */
207 void
AtEOXact_SPI(bool isCommit)208 AtEOXact_SPI(bool isCommit)
209 {
210 /*
211 * Note that memory contexts belonging to SPI stack entries will be freed
212 * automatically, so we can ignore them here. We just need to restore our
213 * static variables to initial state.
214 */
215 if (isCommit && _SPI_connected != -1)
216 ereport(WARNING,
217 (errcode(ERRCODE_WARNING),
218 errmsg("transaction left non-empty SPI stack"),
219 errhint("Check for missing \"SPI_finish\" calls.")));
220
221 _SPI_current = _SPI_stack = NULL;
222 _SPI_stack_depth = 0;
223 _SPI_connected = -1;
224 /* Reset API global variables, too */
225 SPI_processed = 0;
226 SPI_lastoid = InvalidOid;
227 SPI_tuptable = NULL;
228 SPI_result = 0;
229 }
230
231 /*
232 * Clean up SPI state at subtransaction commit or abort.
233 *
234 * During commit, there shouldn't be any unclosed entries remaining from
235 * the current subtransaction; we emit a warning if any are found.
236 */
237 void
AtEOSubXact_SPI(bool isCommit,SubTransactionId mySubid)238 AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
239 {
240 bool found = false;
241
242 while (_SPI_connected >= 0)
243 {
244 _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
245
246 if (connection->connectSubid != mySubid)
247 break; /* couldn't be any underneath it either */
248
249 found = true;
250
251 /*
252 * Release procedure memory explicitly (see note in SPI_connect)
253 */
254 if (connection->execCxt)
255 {
256 MemoryContextDelete(connection->execCxt);
257 connection->execCxt = NULL;
258 }
259 if (connection->procCxt)
260 {
261 MemoryContextDelete(connection->procCxt);
262 connection->procCxt = NULL;
263 }
264
265 /*
266 * Restore outer global variables and pop the stack entry. Unlike
267 * SPI_finish(), we don't risk switching to memory contexts that might
268 * be already gone.
269 */
270 SPI_processed = connection->outer_processed;
271 SPI_lastoid = connection->outer_lastoid;
272 SPI_tuptable = connection->outer_tuptable;
273 SPI_result = connection->outer_result;
274
275 _SPI_connected--;
276 if (_SPI_connected < 0)
277 _SPI_current = NULL;
278 else
279 _SPI_current = &(_SPI_stack[_SPI_connected]);
280 }
281
282 if (found && isCommit)
283 ereport(WARNING,
284 (errcode(ERRCODE_WARNING),
285 errmsg("subtransaction left non-empty SPI stack"),
286 errhint("Check for missing \"SPI_finish\" calls.")));
287
288 /*
289 * If we are aborting a subtransaction and there is an open SPI context
290 * surrounding the subxact, clean up to prevent memory leakage.
291 */
292 if (_SPI_current && !isCommit)
293 {
294 slist_mutable_iter siter;
295
296 /*
297 * Throw away executor state if current executor operation was started
298 * within current subxact (essentially, force a _SPI_end_call(true)).
299 */
300 if (_SPI_current->execSubid >= mySubid)
301 {
302 _SPI_current->execSubid = InvalidSubTransactionId;
303 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
304 }
305
306 /* throw away any tuple tables created within current subxact */
307 slist_foreach_modify(siter, &_SPI_current->tuptables)
308 {
309 SPITupleTable *tuptable;
310
311 tuptable = slist_container(SPITupleTable, next, siter.cur);
312 if (tuptable->subid >= mySubid)
313 {
314 /*
315 * If we used SPI_freetuptable() here, its internal search of
316 * the tuptables list would make this operation O(N^2).
317 * Instead, just free the tuptable manually. This should
318 * match what SPI_freetuptable() does.
319 */
320 slist_delete_current(&siter);
321 if (tuptable == _SPI_current->tuptable)
322 _SPI_current->tuptable = NULL;
323 if (tuptable == SPI_tuptable)
324 SPI_tuptable = NULL;
325 MemoryContextDelete(tuptable->tuptabcxt);
326 }
327 }
328 }
329 }
330
331
332 /* Parse, plan, and execute a query string */
333 int
SPI_execute(const char * src,bool read_only,long tcount)334 SPI_execute(const char *src, bool read_only, long tcount)
335 {
336 _SPI_plan plan;
337 int res;
338
339 if (src == NULL || tcount < 0)
340 return SPI_ERROR_ARGUMENT;
341
342 res = _SPI_begin_call(true);
343 if (res < 0)
344 return res;
345
346 memset(&plan, 0, sizeof(_SPI_plan));
347 plan.magic = _SPI_PLAN_MAGIC;
348 plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
349
350 _SPI_prepare_oneshot_plan(src, &plan);
351
352 res = _SPI_execute_plan(&plan, NULL,
353 InvalidSnapshot, InvalidSnapshot,
354 read_only, true, tcount);
355
356 _SPI_end_call(true);
357 return res;
358 }
359
360 /* Obsolete version of SPI_execute */
361 int
SPI_exec(const char * src,long tcount)362 SPI_exec(const char *src, long tcount)
363 {
364 return SPI_execute(src, false, tcount);
365 }
366
367 /* Execute a previously prepared plan */
368 int
SPI_execute_plan(SPIPlanPtr plan,Datum * Values,const char * Nulls,bool read_only,long tcount)369 SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
370 bool read_only, long tcount)
371 {
372 int res;
373
374 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
375 return SPI_ERROR_ARGUMENT;
376
377 if (plan->nargs > 0 && Values == NULL)
378 return SPI_ERROR_PARAM;
379
380 res = _SPI_begin_call(true);
381 if (res < 0)
382 return res;
383
384 res = _SPI_execute_plan(plan,
385 _SPI_convert_params(plan->nargs, plan->argtypes,
386 Values, Nulls),
387 InvalidSnapshot, InvalidSnapshot,
388 read_only, true, tcount);
389
390 _SPI_end_call(true);
391 return res;
392 }
393
394 /* Obsolete version of SPI_execute_plan */
395 int
SPI_execp(SPIPlanPtr plan,Datum * Values,const char * Nulls,long tcount)396 SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
397 {
398 return SPI_execute_plan(plan, Values, Nulls, false, tcount);
399 }
400
401 /* Execute a previously prepared plan */
402 int
SPI_execute_plan_with_paramlist(SPIPlanPtr plan,ParamListInfo params,bool read_only,long tcount)403 SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
404 bool read_only, long tcount)
405 {
406 int res;
407
408 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
409 return SPI_ERROR_ARGUMENT;
410
411 res = _SPI_begin_call(true);
412 if (res < 0)
413 return res;
414
415 res = _SPI_execute_plan(plan, params,
416 InvalidSnapshot, InvalidSnapshot,
417 read_only, true, tcount);
418
419 _SPI_end_call(true);
420 return res;
421 }
422
423 /*
424 * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
425 * the caller to specify exactly which snapshots to use, which will be
426 * registered here. Also, the caller may specify that AFTER triggers should be
427 * queued as part of the outer query rather than being fired immediately at the
428 * end of the command.
429 *
430 * This is currently not documented in spi.sgml because it is only intended
431 * for use by RI triggers.
432 *
433 * Passing snapshot == InvalidSnapshot will select the normal behavior of
434 * fetching a new snapshot for each query.
435 */
436 int
SPI_execute_snapshot(SPIPlanPtr plan,Datum * Values,const char * Nulls,Snapshot snapshot,Snapshot crosscheck_snapshot,bool read_only,bool fire_triggers,long tcount)437 SPI_execute_snapshot(SPIPlanPtr plan,
438 Datum *Values, const char *Nulls,
439 Snapshot snapshot, Snapshot crosscheck_snapshot,
440 bool read_only, bool fire_triggers, long tcount)
441 {
442 int res;
443
444 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
445 return SPI_ERROR_ARGUMENT;
446
447 if (plan->nargs > 0 && Values == NULL)
448 return SPI_ERROR_PARAM;
449
450 res = _SPI_begin_call(true);
451 if (res < 0)
452 return res;
453
454 res = _SPI_execute_plan(plan,
455 _SPI_convert_params(plan->nargs, plan->argtypes,
456 Values, Nulls),
457 snapshot, crosscheck_snapshot,
458 read_only, fire_triggers, tcount);
459
460 _SPI_end_call(true);
461 return res;
462 }
463
464 /*
465 * SPI_execute_with_args -- plan and execute a query with supplied arguments
466 *
467 * This is functionally equivalent to SPI_prepare followed by
468 * SPI_execute_plan.
469 */
470 int
SPI_execute_with_args(const char * src,int nargs,Oid * argtypes,Datum * Values,const char * Nulls,bool read_only,long tcount)471 SPI_execute_with_args(const char *src,
472 int nargs, Oid *argtypes,
473 Datum *Values, const char *Nulls,
474 bool read_only, long tcount)
475 {
476 int res;
477 _SPI_plan plan;
478 ParamListInfo paramLI;
479
480 if (src == NULL || nargs < 0 || tcount < 0)
481 return SPI_ERROR_ARGUMENT;
482
483 if (nargs > 0 && (argtypes == NULL || Values == NULL))
484 return SPI_ERROR_PARAM;
485
486 res = _SPI_begin_call(true);
487 if (res < 0)
488 return res;
489
490 memset(&plan, 0, sizeof(_SPI_plan));
491 plan.magic = _SPI_PLAN_MAGIC;
492 plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
493 plan.nargs = nargs;
494 plan.argtypes = argtypes;
495 plan.parserSetup = NULL;
496 plan.parserSetupArg = NULL;
497
498 paramLI = _SPI_convert_params(nargs, argtypes,
499 Values, Nulls);
500
501 _SPI_prepare_oneshot_plan(src, &plan);
502
503 res = _SPI_execute_plan(&plan, paramLI,
504 InvalidSnapshot, InvalidSnapshot,
505 read_only, true, tcount);
506
507 _SPI_end_call(true);
508 return res;
509 }
510
511 SPIPlanPtr
SPI_prepare(const char * src,int nargs,Oid * argtypes)512 SPI_prepare(const char *src, int nargs, Oid *argtypes)
513 {
514 return SPI_prepare_cursor(src, nargs, argtypes, 0);
515 }
516
517 SPIPlanPtr
SPI_prepare_cursor(const char * src,int nargs,Oid * argtypes,int cursorOptions)518 SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
519 int cursorOptions)
520 {
521 _SPI_plan plan;
522 SPIPlanPtr result;
523
524 if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
525 {
526 SPI_result = SPI_ERROR_ARGUMENT;
527 return NULL;
528 }
529
530 SPI_result = _SPI_begin_call(true);
531 if (SPI_result < 0)
532 return NULL;
533
534 memset(&plan, 0, sizeof(_SPI_plan));
535 plan.magic = _SPI_PLAN_MAGIC;
536 plan.cursor_options = cursorOptions;
537 plan.nargs = nargs;
538 plan.argtypes = argtypes;
539 plan.parserSetup = NULL;
540 plan.parserSetupArg = NULL;
541
542 _SPI_prepare_plan(src, &plan);
543
544 /* copy plan to procedure context */
545 result = _SPI_make_plan_non_temp(&plan);
546
547 _SPI_end_call(true);
548
549 return result;
550 }
551
552 SPIPlanPtr
SPI_prepare_params(const char * src,ParserSetupHook parserSetup,void * parserSetupArg,int cursorOptions)553 SPI_prepare_params(const char *src,
554 ParserSetupHook parserSetup,
555 void *parserSetupArg,
556 int cursorOptions)
557 {
558 _SPI_plan plan;
559 SPIPlanPtr result;
560
561 if (src == NULL)
562 {
563 SPI_result = SPI_ERROR_ARGUMENT;
564 return NULL;
565 }
566
567 SPI_result = _SPI_begin_call(true);
568 if (SPI_result < 0)
569 return NULL;
570
571 memset(&plan, 0, sizeof(_SPI_plan));
572 plan.magic = _SPI_PLAN_MAGIC;
573 plan.cursor_options = cursorOptions;
574 plan.nargs = 0;
575 plan.argtypes = NULL;
576 plan.parserSetup = parserSetup;
577 plan.parserSetupArg = parserSetupArg;
578
579 _SPI_prepare_plan(src, &plan);
580
581 /* copy plan to procedure context */
582 result = _SPI_make_plan_non_temp(&plan);
583
584 _SPI_end_call(true);
585
586 return result;
587 }
588
589 int
SPI_keepplan(SPIPlanPtr plan)590 SPI_keepplan(SPIPlanPtr plan)
591 {
592 ListCell *lc;
593
594 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
595 plan->saved || plan->oneshot)
596 return SPI_ERROR_ARGUMENT;
597
598 /*
599 * Mark it saved, reparent it under CacheMemoryContext, and mark all the
600 * component CachedPlanSources as saved. This sequence cannot fail
601 * partway through, so there's no risk of long-term memory leakage.
602 */
603 plan->saved = true;
604 MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
605
606 foreach(lc, plan->plancache_list)
607 {
608 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
609
610 SaveCachedPlan(plansource);
611 }
612
613 return 0;
614 }
615
616 SPIPlanPtr
SPI_saveplan(SPIPlanPtr plan)617 SPI_saveplan(SPIPlanPtr plan)
618 {
619 SPIPlanPtr newplan;
620
621 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
622 {
623 SPI_result = SPI_ERROR_ARGUMENT;
624 return NULL;
625 }
626
627 SPI_result = _SPI_begin_call(false); /* don't change context */
628 if (SPI_result < 0)
629 return NULL;
630
631 newplan = _SPI_save_plan(plan);
632
633 SPI_result = _SPI_end_call(false);
634
635 return newplan;
636 }
637
638 int
SPI_freeplan(SPIPlanPtr plan)639 SPI_freeplan(SPIPlanPtr plan)
640 {
641 ListCell *lc;
642
643 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
644 return SPI_ERROR_ARGUMENT;
645
646 /* Release the plancache entries */
647 foreach(lc, plan->plancache_list)
648 {
649 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
650
651 DropCachedPlan(plansource);
652 }
653
654 /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
655 MemoryContextDelete(plan->plancxt);
656
657 return 0;
658 }
659
660 HeapTuple
SPI_copytuple(HeapTuple tuple)661 SPI_copytuple(HeapTuple tuple)
662 {
663 MemoryContext oldcxt;
664 HeapTuple ctuple;
665
666 if (tuple == NULL)
667 {
668 SPI_result = SPI_ERROR_ARGUMENT;
669 return NULL;
670 }
671
672 if (_SPI_current == NULL)
673 {
674 SPI_result = SPI_ERROR_UNCONNECTED;
675 return NULL;
676 }
677
678 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
679
680 ctuple = heap_copytuple(tuple);
681
682 MemoryContextSwitchTo(oldcxt);
683
684 return ctuple;
685 }
686
687 HeapTupleHeader
SPI_returntuple(HeapTuple tuple,TupleDesc tupdesc)688 SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
689 {
690 MemoryContext oldcxt;
691 HeapTupleHeader dtup;
692
693 if (tuple == NULL || tupdesc == NULL)
694 {
695 SPI_result = SPI_ERROR_ARGUMENT;
696 return NULL;
697 }
698
699 if (_SPI_current == NULL)
700 {
701 SPI_result = SPI_ERROR_UNCONNECTED;
702 return NULL;
703 }
704
705 /* For RECORD results, make sure a typmod has been assigned */
706 if (tupdesc->tdtypeid == RECORDOID &&
707 tupdesc->tdtypmod < 0)
708 assign_record_type_typmod(tupdesc);
709
710 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
711
712 dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
713
714 MemoryContextSwitchTo(oldcxt);
715
716 return dtup;
717 }
718
719 HeapTuple
SPI_modifytuple(Relation rel,HeapTuple tuple,int natts,int * attnum,Datum * Values,const char * Nulls)720 SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
721 Datum *Values, const char *Nulls)
722 {
723 MemoryContext oldcxt;
724 HeapTuple mtuple;
725 int numberOfAttributes;
726 Datum *v;
727 bool *n;
728 int i;
729
730 if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
731 {
732 SPI_result = SPI_ERROR_ARGUMENT;
733 return NULL;
734 }
735
736 if (_SPI_current == NULL)
737 {
738 SPI_result = SPI_ERROR_UNCONNECTED;
739 return NULL;
740 }
741
742 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
743
744 SPI_result = 0;
745
746 numberOfAttributes = rel->rd_att->natts;
747 v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
748 n = (bool *) palloc(numberOfAttributes * sizeof(bool));
749
750 /* fetch old values and nulls */
751 heap_deform_tuple(tuple, rel->rd_att, v, n);
752
753 /* replace values and nulls */
754 for (i = 0; i < natts; i++)
755 {
756 if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
757 break;
758 v[attnum[i] - 1] = Values[i];
759 n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? true : false;
760 }
761
762 if (i == natts) /* no errors in *attnum */
763 {
764 mtuple = heap_form_tuple(rel->rd_att, v, n);
765
766 /*
767 * copy the identification info of the old tuple: t_ctid, t_self, and
768 * OID (if any)
769 */
770 mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
771 mtuple->t_self = tuple->t_self;
772 mtuple->t_tableOid = tuple->t_tableOid;
773 if (rel->rd_att->tdhasoid)
774 HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
775 }
776 else
777 {
778 mtuple = NULL;
779 SPI_result = SPI_ERROR_NOATTRIBUTE;
780 }
781
782 pfree(v);
783 pfree(n);
784
785 MemoryContextSwitchTo(oldcxt);
786
787 return mtuple;
788 }
789
790 int
SPI_fnumber(TupleDesc tupdesc,const char * fname)791 SPI_fnumber(TupleDesc tupdesc, const char *fname)
792 {
793 int res;
794 Form_pg_attribute sysatt;
795
796 for (res = 0; res < tupdesc->natts; res++)
797 {
798 if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0 &&
799 !tupdesc->attrs[res]->attisdropped)
800 return res + 1;
801 }
802
803 sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
804 if (sysatt != NULL)
805 return sysatt->attnum;
806
807 /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
808 return SPI_ERROR_NOATTRIBUTE;
809 }
810
811 char *
SPI_fname(TupleDesc tupdesc,int fnumber)812 SPI_fname(TupleDesc tupdesc, int fnumber)
813 {
814 Form_pg_attribute att;
815
816 SPI_result = 0;
817
818 if (fnumber > tupdesc->natts || fnumber == 0 ||
819 fnumber <= FirstLowInvalidHeapAttributeNumber)
820 {
821 SPI_result = SPI_ERROR_NOATTRIBUTE;
822 return NULL;
823 }
824
825 if (fnumber > 0)
826 att = tupdesc->attrs[fnumber - 1];
827 else
828 att = SystemAttributeDefinition(fnumber, true);
829
830 return pstrdup(NameStr(att->attname));
831 }
832
833 char *
SPI_getvalue(HeapTuple tuple,TupleDesc tupdesc,int fnumber)834 SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
835 {
836 Datum val;
837 bool isnull;
838 Oid typoid,
839 foutoid;
840 bool typisvarlena;
841
842 SPI_result = 0;
843
844 if (fnumber > tupdesc->natts || fnumber == 0 ||
845 fnumber <= FirstLowInvalidHeapAttributeNumber)
846 {
847 SPI_result = SPI_ERROR_NOATTRIBUTE;
848 return NULL;
849 }
850
851 val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
852 if (isnull)
853 return NULL;
854
855 if (fnumber > 0)
856 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
857 else
858 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
859
860 getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
861
862 return OidOutputFunctionCall(foutoid, val);
863 }
864
865 Datum
SPI_getbinval(HeapTuple tuple,TupleDesc tupdesc,int fnumber,bool * isnull)866 SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
867 {
868 SPI_result = 0;
869
870 if (fnumber > tupdesc->natts || fnumber == 0 ||
871 fnumber <= FirstLowInvalidHeapAttributeNumber)
872 {
873 SPI_result = SPI_ERROR_NOATTRIBUTE;
874 *isnull = true;
875 return (Datum) NULL;
876 }
877
878 return heap_getattr(tuple, fnumber, tupdesc, isnull);
879 }
880
881 char *
SPI_gettype(TupleDesc tupdesc,int fnumber)882 SPI_gettype(TupleDesc tupdesc, int fnumber)
883 {
884 Oid typoid;
885 HeapTuple typeTuple;
886 char *result;
887
888 SPI_result = 0;
889
890 if (fnumber > tupdesc->natts || fnumber == 0 ||
891 fnumber <= FirstLowInvalidHeapAttributeNumber)
892 {
893 SPI_result = SPI_ERROR_NOATTRIBUTE;
894 return NULL;
895 }
896
897 if (fnumber > 0)
898 typoid = tupdesc->attrs[fnumber - 1]->atttypid;
899 else
900 typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
901
902 typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
903
904 if (!HeapTupleIsValid(typeTuple))
905 {
906 SPI_result = SPI_ERROR_TYPUNKNOWN;
907 return NULL;
908 }
909
910 result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
911 ReleaseSysCache(typeTuple);
912 return result;
913 }
914
915 /*
916 * Get the data type OID for a column.
917 *
918 * There's nothing similar for typmod and typcollation. The rare consumers
919 * thereof should inspect the TupleDesc directly.
920 */
921 Oid
SPI_gettypeid(TupleDesc tupdesc,int fnumber)922 SPI_gettypeid(TupleDesc tupdesc, int fnumber)
923 {
924 SPI_result = 0;
925
926 if (fnumber > tupdesc->natts || fnumber == 0 ||
927 fnumber <= FirstLowInvalidHeapAttributeNumber)
928 {
929 SPI_result = SPI_ERROR_NOATTRIBUTE;
930 return InvalidOid;
931 }
932
933 if (fnumber > 0)
934 return tupdesc->attrs[fnumber - 1]->atttypid;
935 else
936 return (SystemAttributeDefinition(fnumber, true))->atttypid;
937 }
938
939 char *
SPI_getrelname(Relation rel)940 SPI_getrelname(Relation rel)
941 {
942 return pstrdup(RelationGetRelationName(rel));
943 }
944
945 char *
SPI_getnspname(Relation rel)946 SPI_getnspname(Relation rel)
947 {
948 return get_namespace_name(RelationGetNamespace(rel));
949 }
950
951 void *
SPI_palloc(Size size)952 SPI_palloc(Size size)
953 {
954 if (_SPI_current == NULL)
955 elog(ERROR, "SPI_palloc called while not connected to SPI");
956
957 return MemoryContextAlloc(_SPI_current->savedcxt, size);
958 }
959
960 void *
SPI_repalloc(void * pointer,Size size)961 SPI_repalloc(void *pointer, Size size)
962 {
963 /* No longer need to worry which context chunk was in... */
964 return repalloc(pointer, size);
965 }
966
967 void
SPI_pfree(void * pointer)968 SPI_pfree(void *pointer)
969 {
970 /* No longer need to worry which context chunk was in... */
971 pfree(pointer);
972 }
973
974 Datum
SPI_datumTransfer(Datum value,bool typByVal,int typLen)975 SPI_datumTransfer(Datum value, bool typByVal, int typLen)
976 {
977 MemoryContext oldcxt;
978 Datum result;
979
980 if (_SPI_current == NULL)
981 elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
982
983 oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
984
985 result = datumTransfer(value, typByVal, typLen);
986
987 MemoryContextSwitchTo(oldcxt);
988
989 return result;
990 }
991
992 void
SPI_freetuple(HeapTuple tuple)993 SPI_freetuple(HeapTuple tuple)
994 {
995 /* No longer need to worry which context tuple was in... */
996 heap_freetuple(tuple);
997 }
998
999 void
SPI_freetuptable(SPITupleTable * tuptable)1000 SPI_freetuptable(SPITupleTable *tuptable)
1001 {
1002 bool found = false;
1003
1004 /* ignore call if NULL pointer */
1005 if (tuptable == NULL)
1006 return;
1007
1008 /*
1009 * Search only the topmost SPI context for a matching tuple table.
1010 */
1011 if (_SPI_current != NULL)
1012 {
1013 slist_mutable_iter siter;
1014
1015 /* find tuptable in active list, then remove it */
1016 slist_foreach_modify(siter, &_SPI_current->tuptables)
1017 {
1018 SPITupleTable *tt;
1019
1020 tt = slist_container(SPITupleTable, next, siter.cur);
1021 if (tt == tuptable)
1022 {
1023 slist_delete_current(&siter);
1024 found = true;
1025 break;
1026 }
1027 }
1028 }
1029
1030 /*
1031 * Refuse the deletion if we didn't find it in the topmost SPI context.
1032 * This is primarily a guard against double deletion, but might prevent
1033 * other errors as well. Since the worst consequence of not deleting a
1034 * tuptable would be a transient memory leak, this is just a WARNING.
1035 */
1036 if (!found)
1037 {
1038 elog(WARNING, "attempt to delete invalid SPITupleTable %p", tuptable);
1039 return;
1040 }
1041
1042 /* for safety, reset global variables that might point at tuptable */
1043 if (tuptable == _SPI_current->tuptable)
1044 _SPI_current->tuptable = NULL;
1045 if (tuptable == SPI_tuptable)
1046 SPI_tuptable = NULL;
1047
1048 /* release all memory belonging to tuptable */
1049 MemoryContextDelete(tuptable->tuptabcxt);
1050 }
1051
1052
1053 /*
1054 * SPI_cursor_open()
1055 *
1056 * Open a prepared SPI plan as a portal
1057 */
1058 Portal
SPI_cursor_open(const char * name,SPIPlanPtr plan,Datum * Values,const char * Nulls,bool read_only)1059 SPI_cursor_open(const char *name, SPIPlanPtr plan,
1060 Datum *Values, const char *Nulls,
1061 bool read_only)
1062 {
1063 Portal portal;
1064 ParamListInfo paramLI;
1065
1066 /* build transient ParamListInfo in caller's context */
1067 paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
1068 Values, Nulls);
1069
1070 portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
1071
1072 /* done with the transient ParamListInfo */
1073 if (paramLI)
1074 pfree(paramLI);
1075
1076 return portal;
1077 }
1078
1079
1080 /*
1081 * SPI_cursor_open_with_args()
1082 *
1083 * Parse and plan a query and open it as a portal.
1084 */
1085 Portal
SPI_cursor_open_with_args(const char * name,const char * src,int nargs,Oid * argtypes,Datum * Values,const char * Nulls,bool read_only,int cursorOptions)1086 SPI_cursor_open_with_args(const char *name,
1087 const char *src,
1088 int nargs, Oid *argtypes,
1089 Datum *Values, const char *Nulls,
1090 bool read_only, int cursorOptions)
1091 {
1092 Portal result;
1093 _SPI_plan plan;
1094 ParamListInfo paramLI;
1095
1096 if (src == NULL || nargs < 0)
1097 elog(ERROR, "SPI_cursor_open_with_args called with invalid arguments");
1098
1099 if (nargs > 0 && (argtypes == NULL || Values == NULL))
1100 elog(ERROR, "SPI_cursor_open_with_args called with missing parameters");
1101
1102 SPI_result = _SPI_begin_call(true);
1103 if (SPI_result < 0)
1104 elog(ERROR, "SPI_cursor_open_with_args called while not connected");
1105
1106 memset(&plan, 0, sizeof(_SPI_plan));
1107 plan.magic = _SPI_PLAN_MAGIC;
1108 plan.cursor_options = cursorOptions;
1109 plan.nargs = nargs;
1110 plan.argtypes = argtypes;
1111 plan.parserSetup = NULL;
1112 plan.parserSetupArg = NULL;
1113
1114 /* build transient ParamListInfo in executor context */
1115 paramLI = _SPI_convert_params(nargs, argtypes,
1116 Values, Nulls);
1117
1118 _SPI_prepare_plan(src, &plan);
1119
1120 /* We needn't copy the plan; SPI_cursor_open_internal will do so */
1121
1122 result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
1123
1124 /* And clean up */
1125 _SPI_end_call(true);
1126
1127 return result;
1128 }
1129
1130
1131 /*
1132 * SPI_cursor_open_with_paramlist()
1133 *
1134 * Same as SPI_cursor_open except that parameters (if any) are passed
1135 * as a ParamListInfo, which supports dynamic parameter set determination
1136 */
1137 Portal
SPI_cursor_open_with_paramlist(const char * name,SPIPlanPtr plan,ParamListInfo params,bool read_only)1138 SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
1139 ParamListInfo params, bool read_only)
1140 {
1141 return SPI_cursor_open_internal(name, plan, params, read_only);
1142 }
1143
1144
1145 /*
1146 * SPI_cursor_open_internal()
1147 *
1148 * Common code for SPI_cursor_open variants
1149 */
1150 static Portal
SPI_cursor_open_internal(const char * name,SPIPlanPtr plan,ParamListInfo paramLI,bool read_only)1151 SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
1152 ParamListInfo paramLI, bool read_only)
1153 {
1154 CachedPlanSource *plansource;
1155 CachedPlan *cplan;
1156 List *stmt_list;
1157 char *query_string;
1158 Snapshot snapshot;
1159 MemoryContext oldcontext;
1160 Portal portal;
1161 ErrorContextCallback spierrcontext;
1162
1163 /*
1164 * Check that the plan is something the Portal code will special-case as
1165 * returning one tupleset.
1166 */
1167 if (!SPI_is_cursor_plan(plan))
1168 {
1169 /* try to give a good error message */
1170 if (list_length(plan->plancache_list) != 1)
1171 ereport(ERROR,
1172 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
1173 errmsg("cannot open multi-query plan as cursor")));
1174 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1175 ereport(ERROR,
1176 (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
1177 /* translator: %s is name of a SQL command, eg INSERT */
1178 errmsg("cannot open %s query as cursor",
1179 plansource->commandTag)));
1180 }
1181
1182 Assert(list_length(plan->plancache_list) == 1);
1183 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1184
1185 /* Push the SPI stack */
1186 if (_SPI_begin_call(true) < 0)
1187 elog(ERROR, "SPI_cursor_open called while not connected");
1188
1189 /* Reset SPI result (note we deliberately don't touch lastoid) */
1190 SPI_processed = 0;
1191 SPI_tuptable = NULL;
1192 _SPI_current->processed = 0;
1193 _SPI_current->tuptable = NULL;
1194
1195 /* Create the portal */
1196 if (name == NULL || name[0] == '\0')
1197 {
1198 /* Use a random nonconflicting name */
1199 portal = CreateNewPortal();
1200 }
1201 else
1202 {
1203 /* In this path, error if portal of same name already exists */
1204 portal = CreatePortal(name, false, false);
1205 }
1206
1207 /* Copy the plan's query string into the portal */
1208 query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
1209 plansource->query_string);
1210
1211 /*
1212 * Setup error traceback support for ereport(), in case GetCachedPlan
1213 * throws an error.
1214 */
1215 spierrcontext.callback = _SPI_error_callback;
1216 spierrcontext.arg = (void *) plansource->query_string;
1217 spierrcontext.previous = error_context_stack;
1218 error_context_stack = &spierrcontext;
1219
1220 /*
1221 * Note: for a saved plan, we mustn't have any failure occur between
1222 * GetCachedPlan and PortalDefineQuery; that would result in leaking our
1223 * plancache refcount.
1224 */
1225
1226 /* Replan if needed, and increment plan refcount for portal */
1227 cplan = GetCachedPlan(plansource, paramLI, false, _SPI_current->queryEnv);
1228 stmt_list = cplan->stmt_list;
1229
1230 if (!plan->saved)
1231 {
1232 /*
1233 * We don't want the portal to depend on an unsaved CachedPlanSource,
1234 * so must copy the plan into the portal's context. An error here
1235 * will result in leaking our refcount on the plan, but it doesn't
1236 * matter because the plan is unsaved and hence transient anyway.
1237 */
1238 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
1239 stmt_list = copyObject(stmt_list);
1240 MemoryContextSwitchTo(oldcontext);
1241 ReleaseCachedPlan(cplan, false);
1242 cplan = NULL; /* portal shouldn't depend on cplan */
1243 }
1244
1245 /*
1246 * Set up the portal.
1247 */
1248 PortalDefineQuery(portal,
1249 NULL, /* no statement name */
1250 query_string,
1251 plansource->commandTag,
1252 stmt_list,
1253 cplan);
1254
1255 /*
1256 * Set up options for portal. Default SCROLL type is chosen the same way
1257 * as PerformCursorOpen does it.
1258 */
1259 portal->cursorOptions = plan->cursor_options;
1260 if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
1261 {
1262 if (list_length(stmt_list) == 1 &&
1263 linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1264 linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL &&
1265 ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree))
1266 portal->cursorOptions |= CURSOR_OPT_SCROLL;
1267 else
1268 portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
1269 }
1270
1271 /*
1272 * Disallow SCROLL with SELECT FOR UPDATE. This is not redundant with the
1273 * check in transformDeclareCursorStmt because the cursor options might
1274 * not have come through there.
1275 */
1276 if (portal->cursorOptions & CURSOR_OPT_SCROLL)
1277 {
1278 if (list_length(stmt_list) == 1 &&
1279 linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1280 linitial_node(PlannedStmt, stmt_list)->rowMarks != NIL)
1281 ereport(ERROR,
1282 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1283 errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"),
1284 errdetail("Scrollable cursors must be READ ONLY.")));
1285 }
1286
1287 /* Make current query environment available to portal at execution time. */
1288 portal->queryEnv = _SPI_current->queryEnv;
1289
1290 /*
1291 * If told to be read-only, or in parallel mode, verify that this query is
1292 * in fact read-only. This can't be done earlier because we need to look
1293 * at the finished, planned queries. (In particular, we don't want to do
1294 * it between GetCachedPlan and PortalDefineQuery, because throwing an
1295 * error between those steps would result in leaking our plancache
1296 * refcount.)
1297 */
1298 if (read_only || IsInParallelMode())
1299 {
1300 ListCell *lc;
1301
1302 foreach(lc, stmt_list)
1303 {
1304 PlannedStmt *pstmt = lfirst_node(PlannedStmt, lc);
1305
1306 if (!CommandIsReadOnly(pstmt))
1307 {
1308 if (read_only)
1309 ereport(ERROR,
1310 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1311 /* translator: %s is a SQL statement name */
1312 errmsg("%s is not allowed in a non-volatile function",
1313 CreateCommandTag((Node *) pstmt))));
1314 else
1315 PreventCommandIfParallelMode(CreateCommandTag((Node *) pstmt));
1316 }
1317 }
1318 }
1319
1320 /* Set up the snapshot to use. */
1321 if (read_only)
1322 snapshot = GetActiveSnapshot();
1323 else
1324 {
1325 CommandCounterIncrement();
1326 snapshot = GetTransactionSnapshot();
1327 }
1328
1329 /*
1330 * If the plan has parameters, copy them into the portal. Note that this
1331 * must be done after revalidating the plan, because in dynamic parameter
1332 * cases the set of parameters could have changed during re-parsing.
1333 */
1334 if (paramLI)
1335 {
1336 oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
1337 paramLI = copyParamList(paramLI);
1338 MemoryContextSwitchTo(oldcontext);
1339 }
1340
1341 /*
1342 * Start portal execution.
1343 */
1344 PortalStart(portal, paramLI, 0, snapshot);
1345
1346 Assert(portal->strategy != PORTAL_MULTI_QUERY);
1347
1348 /* Pop the error context stack */
1349 error_context_stack = spierrcontext.previous;
1350
1351 /* Pop the SPI stack */
1352 _SPI_end_call(true);
1353
1354 /* Return the created portal */
1355 return portal;
1356 }
1357
1358
1359 /*
1360 * SPI_cursor_find()
1361 *
1362 * Find the portal of an existing open cursor
1363 */
1364 Portal
SPI_cursor_find(const char * name)1365 SPI_cursor_find(const char *name)
1366 {
1367 return GetPortalByName(name);
1368 }
1369
1370
1371 /*
1372 * SPI_cursor_fetch()
1373 *
1374 * Fetch rows in a cursor
1375 */
1376 void
SPI_cursor_fetch(Portal portal,bool forward,long count)1377 SPI_cursor_fetch(Portal portal, bool forward, long count)
1378 {
1379 _SPI_cursor_operation(portal,
1380 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1381 CreateDestReceiver(DestSPI));
1382 /* we know that the DestSPI receiver doesn't need a destroy call */
1383 }
1384
1385
1386 /*
1387 * SPI_cursor_move()
1388 *
1389 * Move in a cursor
1390 */
1391 void
SPI_cursor_move(Portal portal,bool forward,long count)1392 SPI_cursor_move(Portal portal, bool forward, long count)
1393 {
1394 _SPI_cursor_operation(portal,
1395 forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1396 None_Receiver);
1397 }
1398
1399
1400 /*
1401 * SPI_scroll_cursor_fetch()
1402 *
1403 * Fetch rows in a scrollable cursor
1404 */
1405 void
SPI_scroll_cursor_fetch(Portal portal,FetchDirection direction,long count)1406 SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
1407 {
1408 _SPI_cursor_operation(portal,
1409 direction, count,
1410 CreateDestReceiver(DestSPI));
1411 /* we know that the DestSPI receiver doesn't need a destroy call */
1412 }
1413
1414
1415 /*
1416 * SPI_scroll_cursor_move()
1417 *
1418 * Move in a scrollable cursor
1419 */
1420 void
SPI_scroll_cursor_move(Portal portal,FetchDirection direction,long count)1421 SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
1422 {
1423 _SPI_cursor_operation(portal, direction, count, None_Receiver);
1424 }
1425
1426
1427 /*
1428 * SPI_cursor_close()
1429 *
1430 * Close a cursor
1431 */
1432 void
SPI_cursor_close(Portal portal)1433 SPI_cursor_close(Portal portal)
1434 {
1435 if (!PortalIsValid(portal))
1436 elog(ERROR, "invalid portal in SPI cursor operation");
1437
1438 PortalDrop(portal, false);
1439 }
1440
1441 /*
1442 * Returns the Oid representing the type id for argument at argIndex. First
1443 * parameter is at index zero.
1444 */
1445 Oid
SPI_getargtypeid(SPIPlanPtr plan,int argIndex)1446 SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
1447 {
1448 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
1449 argIndex < 0 || argIndex >= plan->nargs)
1450 {
1451 SPI_result = SPI_ERROR_ARGUMENT;
1452 return InvalidOid;
1453 }
1454 return plan->argtypes[argIndex];
1455 }
1456
1457 /*
1458 * Returns the number of arguments for the prepared plan.
1459 */
1460 int
SPI_getargcount(SPIPlanPtr plan)1461 SPI_getargcount(SPIPlanPtr plan)
1462 {
1463 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1464 {
1465 SPI_result = SPI_ERROR_ARGUMENT;
1466 return -1;
1467 }
1468 return plan->nargs;
1469 }
1470
1471 /*
1472 * Returns true if the plan contains exactly one command
1473 * and that command returns tuples to the caller (eg, SELECT or
1474 * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
1475 * the result indicates if the command can be used with SPI_cursor_open
1476 *
1477 * Parameters
1478 * plan: A plan previously prepared using SPI_prepare
1479 */
1480 bool
SPI_is_cursor_plan(SPIPlanPtr plan)1481 SPI_is_cursor_plan(SPIPlanPtr plan)
1482 {
1483 CachedPlanSource *plansource;
1484
1485 if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1486 {
1487 SPI_result = SPI_ERROR_ARGUMENT;
1488 return false;
1489 }
1490
1491 if (list_length(plan->plancache_list) != 1)
1492 {
1493 SPI_result = 0;
1494 return false; /* not exactly 1 pre-rewrite command */
1495 }
1496 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1497
1498 /*
1499 * We used to force revalidation of the cached plan here, but that seems
1500 * unnecessary: invalidation could mean a change in the rowtype of the
1501 * tuples returned by a plan, but not whether it returns tuples at all.
1502 */
1503 SPI_result = 0;
1504
1505 /* Does it return tuples? */
1506 if (plansource->resultDesc)
1507 return true;
1508
1509 return false;
1510 }
1511
1512 /*
1513 * SPI_plan_is_valid --- test whether a SPI plan is currently valid
1514 * (that is, not marked as being in need of revalidation).
1515 *
1516 * See notes for CachedPlanIsValid before using this.
1517 */
1518 bool
SPI_plan_is_valid(SPIPlanPtr plan)1519 SPI_plan_is_valid(SPIPlanPtr plan)
1520 {
1521 ListCell *lc;
1522
1523 Assert(plan->magic == _SPI_PLAN_MAGIC);
1524
1525 foreach(lc, plan->plancache_list)
1526 {
1527 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1528
1529 if (!CachedPlanIsValid(plansource))
1530 return false;
1531 }
1532 return true;
1533 }
1534
1535 /*
1536 * SPI_result_code_string --- convert any SPI return code to a string
1537 *
1538 * This is often useful in error messages. Most callers will probably
1539 * only pass negative (error-case) codes, but for generality we recognize
1540 * the success codes too.
1541 */
1542 const char *
SPI_result_code_string(int code)1543 SPI_result_code_string(int code)
1544 {
1545 static char buf[64];
1546
1547 switch (code)
1548 {
1549 case SPI_ERROR_CONNECT:
1550 return "SPI_ERROR_CONNECT";
1551 case SPI_ERROR_COPY:
1552 return "SPI_ERROR_COPY";
1553 case SPI_ERROR_OPUNKNOWN:
1554 return "SPI_ERROR_OPUNKNOWN";
1555 case SPI_ERROR_UNCONNECTED:
1556 return "SPI_ERROR_UNCONNECTED";
1557 case SPI_ERROR_ARGUMENT:
1558 return "SPI_ERROR_ARGUMENT";
1559 case SPI_ERROR_PARAM:
1560 return "SPI_ERROR_PARAM";
1561 case SPI_ERROR_TRANSACTION:
1562 return "SPI_ERROR_TRANSACTION";
1563 case SPI_ERROR_NOATTRIBUTE:
1564 return "SPI_ERROR_NOATTRIBUTE";
1565 case SPI_ERROR_NOOUTFUNC:
1566 return "SPI_ERROR_NOOUTFUNC";
1567 case SPI_ERROR_TYPUNKNOWN:
1568 return "SPI_ERROR_TYPUNKNOWN";
1569 case SPI_ERROR_REL_DUPLICATE:
1570 return "SPI_ERROR_REL_DUPLICATE";
1571 case SPI_ERROR_REL_NOT_FOUND:
1572 return "SPI_ERROR_REL_NOT_FOUND";
1573 case SPI_OK_CONNECT:
1574 return "SPI_OK_CONNECT";
1575 case SPI_OK_FINISH:
1576 return "SPI_OK_FINISH";
1577 case SPI_OK_FETCH:
1578 return "SPI_OK_FETCH";
1579 case SPI_OK_UTILITY:
1580 return "SPI_OK_UTILITY";
1581 case SPI_OK_SELECT:
1582 return "SPI_OK_SELECT";
1583 case SPI_OK_SELINTO:
1584 return "SPI_OK_SELINTO";
1585 case SPI_OK_INSERT:
1586 return "SPI_OK_INSERT";
1587 case SPI_OK_DELETE:
1588 return "SPI_OK_DELETE";
1589 case SPI_OK_UPDATE:
1590 return "SPI_OK_UPDATE";
1591 case SPI_OK_CURSOR:
1592 return "SPI_OK_CURSOR";
1593 case SPI_OK_INSERT_RETURNING:
1594 return "SPI_OK_INSERT_RETURNING";
1595 case SPI_OK_DELETE_RETURNING:
1596 return "SPI_OK_DELETE_RETURNING";
1597 case SPI_OK_UPDATE_RETURNING:
1598 return "SPI_OK_UPDATE_RETURNING";
1599 case SPI_OK_REWRITTEN:
1600 return "SPI_OK_REWRITTEN";
1601 case SPI_OK_REL_REGISTER:
1602 return "SPI_OK_REL_REGISTER";
1603 case SPI_OK_REL_UNREGISTER:
1604 return "SPI_OK_REL_UNREGISTER";
1605 }
1606 /* Unrecognized code ... return something useful ... */
1607 sprintf(buf, "Unrecognized SPI code %d", code);
1608 return buf;
1609 }
1610
1611 /*
1612 * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
1613 * CachedPlanSources.
1614 *
1615 * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
1616 * look directly into the SPIPlan for itself). It's not documented in
1617 * spi.sgml because we'd just as soon not have too many places using this.
1618 */
1619 List *
SPI_plan_get_plan_sources(SPIPlanPtr plan)1620 SPI_plan_get_plan_sources(SPIPlanPtr plan)
1621 {
1622 Assert(plan->magic == _SPI_PLAN_MAGIC);
1623 return plan->plancache_list;
1624 }
1625
1626 /*
1627 * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
1628 * if the SPI plan contains exactly one CachedPlanSource. If not,
1629 * return NULL. Caller is responsible for doing ReleaseCachedPlan().
1630 *
1631 * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
1632 * look directly into the SPIPlan for itself). It's not documented in
1633 * spi.sgml because we'd just as soon not have too many places using this.
1634 */
1635 CachedPlan *
SPI_plan_get_cached_plan(SPIPlanPtr plan)1636 SPI_plan_get_cached_plan(SPIPlanPtr plan)
1637 {
1638 CachedPlanSource *plansource;
1639 CachedPlan *cplan;
1640 ErrorContextCallback spierrcontext;
1641
1642 Assert(plan->magic == _SPI_PLAN_MAGIC);
1643
1644 /* Can't support one-shot plans here */
1645 if (plan->oneshot)
1646 return NULL;
1647
1648 /* Must have exactly one CachedPlanSource */
1649 if (list_length(plan->plancache_list) != 1)
1650 return NULL;
1651 plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1652
1653 /* Setup error traceback support for ereport() */
1654 spierrcontext.callback = _SPI_error_callback;
1655 spierrcontext.arg = (void *) plansource->query_string;
1656 spierrcontext.previous = error_context_stack;
1657 error_context_stack = &spierrcontext;
1658
1659 /* Get the generic plan for the query */
1660 cplan = GetCachedPlan(plansource, NULL, plan->saved,
1661 _SPI_current->queryEnv);
1662 Assert(cplan == plansource->gplan);
1663
1664 /* Pop the error context stack */
1665 error_context_stack = spierrcontext.previous;
1666
1667 return cplan;
1668 }
1669
1670
1671 /* =================== private functions =================== */
1672
1673 /*
1674 * spi_dest_startup
1675 * Initialize to receive tuples from Executor into SPITupleTable
1676 * of current SPI procedure
1677 */
1678 void
spi_dest_startup(DestReceiver * self,int operation,TupleDesc typeinfo)1679 spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
1680 {
1681 SPITupleTable *tuptable;
1682 MemoryContext oldcxt;
1683 MemoryContext tuptabcxt;
1684
1685 if (_SPI_current == NULL)
1686 elog(ERROR, "spi_dest_startup called while not connected to SPI");
1687
1688 if (_SPI_current->tuptable != NULL)
1689 elog(ERROR, "improper call to spi_dest_startup");
1690
1691 /* We create the tuple table context as a child of procCxt */
1692
1693 oldcxt = _SPI_procmem(); /* switch to procedure memory context */
1694
1695 tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
1696 "SPI TupTable",
1697 ALLOCSET_DEFAULT_SIZES);
1698 MemoryContextSwitchTo(tuptabcxt);
1699
1700 _SPI_current->tuptable = tuptable = (SPITupleTable *)
1701 palloc0(sizeof(SPITupleTable));
1702 tuptable->tuptabcxt = tuptabcxt;
1703 tuptable->subid = GetCurrentSubTransactionId();
1704
1705 /*
1706 * The tuptable is now valid enough to be freed by AtEOSubXact_SPI, so put
1707 * it onto the SPI context's tuptables list. This will ensure it's not
1708 * leaked even in the unlikely event the following few lines fail.
1709 */
1710 slist_push_head(&_SPI_current->tuptables, &tuptable->next);
1711
1712 /* set up initial allocations */
1713 tuptable->alloced = tuptable->free = 128;
1714 tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
1715 tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
1716
1717 MemoryContextSwitchTo(oldcxt);
1718 }
1719
1720 /*
1721 * spi_printtup
1722 * store tuple retrieved by Executor into SPITupleTable
1723 * of current SPI procedure
1724 */
1725 bool
spi_printtup(TupleTableSlot * slot,DestReceiver * self)1726 spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1727 {
1728 SPITupleTable *tuptable;
1729 MemoryContext oldcxt;
1730
1731 if (_SPI_current == NULL)
1732 elog(ERROR, "spi_printtup called while not connected to SPI");
1733
1734 tuptable = _SPI_current->tuptable;
1735 if (tuptable == NULL)
1736 elog(ERROR, "improper call to spi_printtup");
1737
1738 oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
1739
1740 if (tuptable->free == 0)
1741 {
1742 /* Double the size of the pointer array */
1743 tuptable->free = tuptable->alloced;
1744 tuptable->alloced += tuptable->free;
1745 tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
1746 tuptable->alloced * sizeof(HeapTuple));
1747 }
1748
1749 tuptable->vals[tuptable->alloced - tuptable->free] =
1750 ExecCopySlotTuple(slot);
1751 (tuptable->free)--;
1752
1753 MemoryContextSwitchTo(oldcxt);
1754
1755 return true;
1756 }
1757
1758 /*
1759 * Static functions
1760 */
1761
1762 /*
1763 * Parse and analyze a querystring.
1764 *
1765 * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
1766 * and plan->parserSetupArg) must be valid, as must plan->cursor_options.
1767 *
1768 * Results are stored into *plan (specifically, plan->plancache_list).
1769 * Note that the result data is all in CurrentMemoryContext or child contexts
1770 * thereof; in practice this means it is in the SPI executor context, and
1771 * what we are creating is a "temporary" SPIPlan. Cruft generated during
1772 * parsing is also left in CurrentMemoryContext.
1773 */
1774 static void
_SPI_prepare_plan(const char * src,SPIPlanPtr plan)1775 _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
1776 {
1777 List *raw_parsetree_list;
1778 List *plancache_list;
1779 ListCell *list_item;
1780 ErrorContextCallback spierrcontext;
1781
1782 /*
1783 * Setup error traceback support for ereport()
1784 */
1785 spierrcontext.callback = _SPI_error_callback;
1786 spierrcontext.arg = (void *) src;
1787 spierrcontext.previous = error_context_stack;
1788 error_context_stack = &spierrcontext;
1789
1790 /*
1791 * Parse the request string into a list of raw parse trees.
1792 */
1793 raw_parsetree_list = pg_parse_query(src);
1794
1795 /*
1796 * Do parse analysis and rule rewrite for each raw parsetree, storing the
1797 * results into unsaved plancache entries.
1798 */
1799 plancache_list = NIL;
1800
1801 foreach(list_item, raw_parsetree_list)
1802 {
1803 RawStmt *parsetree = lfirst_node(RawStmt, list_item);
1804 List *stmt_list;
1805 CachedPlanSource *plansource;
1806
1807 /*
1808 * Create the CachedPlanSource before we do parse analysis, since it
1809 * needs to see the unmodified raw parse tree.
1810 */
1811 plansource = CreateCachedPlan(parsetree,
1812 src,
1813 CreateCommandTag(parsetree->stmt));
1814
1815 /*
1816 * Parameter datatypes are driven by parserSetup hook if provided,
1817 * otherwise we use the fixed parameter list.
1818 */
1819 if (plan->parserSetup != NULL)
1820 {
1821 Assert(plan->nargs == 0);
1822 stmt_list = pg_analyze_and_rewrite_params(parsetree,
1823 src,
1824 plan->parserSetup,
1825 plan->parserSetupArg,
1826 _SPI_current->queryEnv);
1827 }
1828 else
1829 {
1830 stmt_list = pg_analyze_and_rewrite(parsetree,
1831 src,
1832 plan->argtypes,
1833 plan->nargs,
1834 _SPI_current->queryEnv);
1835 }
1836
1837 /* Finish filling in the CachedPlanSource */
1838 CompleteCachedPlan(plansource,
1839 stmt_list,
1840 NULL,
1841 plan->argtypes,
1842 plan->nargs,
1843 plan->parserSetup,
1844 plan->parserSetupArg,
1845 plan->cursor_options,
1846 false); /* not fixed result */
1847
1848 plancache_list = lappend(plancache_list, plansource);
1849 }
1850
1851 plan->plancache_list = plancache_list;
1852 plan->oneshot = false;
1853
1854 /*
1855 * Pop the error context stack
1856 */
1857 error_context_stack = spierrcontext.previous;
1858 }
1859
1860 /*
1861 * Parse, but don't analyze, a querystring.
1862 *
1863 * This is a stripped-down version of _SPI_prepare_plan that only does the
1864 * initial raw parsing. It creates "one shot" CachedPlanSources
1865 * that still require parse analysis before execution is possible.
1866 *
1867 * The advantage of using the "one shot" form of CachedPlanSource is that
1868 * we eliminate data copying and invalidation overhead. Postponing parse
1869 * analysis also prevents issues if some of the raw parsetrees are DDL
1870 * commands that affect validity of later parsetrees. Both of these
1871 * attributes are good things for SPI_execute() and similar cases.
1872 *
1873 * Results are stored into *plan (specifically, plan->plancache_list).
1874 * Note that the result data is all in CurrentMemoryContext or child contexts
1875 * thereof; in practice this means it is in the SPI executor context, and
1876 * what we are creating is a "temporary" SPIPlan. Cruft generated during
1877 * parsing is also left in CurrentMemoryContext.
1878 */
1879 static void
_SPI_prepare_oneshot_plan(const char * src,SPIPlanPtr plan)1880 _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
1881 {
1882 List *raw_parsetree_list;
1883 List *plancache_list;
1884 ListCell *list_item;
1885 ErrorContextCallback spierrcontext;
1886
1887 /*
1888 * Setup error traceback support for ereport()
1889 */
1890 spierrcontext.callback = _SPI_error_callback;
1891 spierrcontext.arg = (void *) src;
1892 spierrcontext.previous = error_context_stack;
1893 error_context_stack = &spierrcontext;
1894
1895 /*
1896 * Parse the request string into a list of raw parse trees.
1897 */
1898 raw_parsetree_list = pg_parse_query(src);
1899
1900 /*
1901 * Construct plancache entries, but don't do parse analysis yet.
1902 */
1903 plancache_list = NIL;
1904
1905 foreach(list_item, raw_parsetree_list)
1906 {
1907 RawStmt *parsetree = lfirst_node(RawStmt, list_item);
1908 CachedPlanSource *plansource;
1909
1910 plansource = CreateOneShotCachedPlan(parsetree,
1911 src,
1912 CreateCommandTag(parsetree->stmt));
1913
1914 plancache_list = lappend(plancache_list, plansource);
1915 }
1916
1917 plan->plancache_list = plancache_list;
1918 plan->oneshot = true;
1919
1920 /*
1921 * Pop the error context stack
1922 */
1923 error_context_stack = spierrcontext.previous;
1924 }
1925
1926 /*
1927 * Execute the given plan with the given parameter values
1928 *
1929 * snapshot: query snapshot to use, or InvalidSnapshot for the normal
1930 * behavior of taking a new snapshot for each query.
1931 * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
1932 * read_only: TRUE for read-only execution (no CommandCounterIncrement)
1933 * fire_triggers: TRUE to fire AFTER triggers at end of query (normal case);
1934 * FALSE means any AFTER triggers are postponed to end of outer query
1935 * tcount: execution tuple-count limit, or 0 for none
1936 */
1937 static int
_SPI_execute_plan(SPIPlanPtr plan,ParamListInfo paramLI,Snapshot snapshot,Snapshot crosscheck_snapshot,bool read_only,bool fire_triggers,uint64 tcount)1938 _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
1939 Snapshot snapshot, Snapshot crosscheck_snapshot,
1940 bool read_only, bool fire_triggers, uint64 tcount)
1941 {
1942 int my_res = 0;
1943 uint64 my_processed = 0;
1944 Oid my_lastoid = InvalidOid;
1945 SPITupleTable *my_tuptable = NULL;
1946 int res = 0;
1947 bool pushed_active_snap = false;
1948 ErrorContextCallback spierrcontext;
1949 CachedPlan *cplan = NULL;
1950 ListCell *lc1;
1951
1952 /*
1953 * Setup error traceback support for ereport()
1954 */
1955 spierrcontext.callback = _SPI_error_callback;
1956 spierrcontext.arg = NULL; /* we'll fill this below */
1957 spierrcontext.previous = error_context_stack;
1958 error_context_stack = &spierrcontext;
1959
1960 /*
1961 * We support four distinct snapshot management behaviors:
1962 *
1963 * snapshot != InvalidSnapshot, read_only = true: use exactly the given
1964 * snapshot.
1965 *
1966 * snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
1967 * modified by advancing its command ID before each querytree.
1968 *
1969 * snapshot == InvalidSnapshot, read_only = true: use the entry-time
1970 * ActiveSnapshot, if any (if there isn't one, we run with no snapshot).
1971 *
1972 * snapshot == InvalidSnapshot, read_only = false: take a full new
1973 * snapshot for each user command, and advance its command ID before each
1974 * querytree within the command.
1975 *
1976 * In the first two cases, we can just push the snap onto the stack once
1977 * for the whole plan list.
1978 */
1979 if (snapshot != InvalidSnapshot)
1980 {
1981 if (read_only)
1982 {
1983 PushActiveSnapshot(snapshot);
1984 pushed_active_snap = true;
1985 }
1986 else
1987 {
1988 /* Make sure we have a private copy of the snapshot to modify */
1989 PushCopiedSnapshot(snapshot);
1990 pushed_active_snap = true;
1991 }
1992 }
1993
1994 foreach(lc1, plan->plancache_list)
1995 {
1996 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
1997 List *stmt_list;
1998 ListCell *lc2;
1999
2000 spierrcontext.arg = (void *) plansource->query_string;
2001
2002 /*
2003 * If this is a one-shot plan, we still need to do parse analysis.
2004 */
2005 if (plan->oneshot)
2006 {
2007 RawStmt *parsetree = plansource->raw_parse_tree;
2008 const char *src = plansource->query_string;
2009 List *stmt_list;
2010
2011 /*
2012 * Parameter datatypes are driven by parserSetup hook if provided,
2013 * otherwise we use the fixed parameter list.
2014 */
2015 if (parsetree == NULL)
2016 stmt_list = NIL;
2017 else if (plan->parserSetup != NULL)
2018 {
2019 Assert(plan->nargs == 0);
2020 stmt_list = pg_analyze_and_rewrite_params(parsetree,
2021 src,
2022 plan->parserSetup,
2023 plan->parserSetupArg,
2024 _SPI_current->queryEnv);
2025 }
2026 else
2027 {
2028 stmt_list = pg_analyze_and_rewrite(parsetree,
2029 src,
2030 plan->argtypes,
2031 plan->nargs,
2032 _SPI_current->queryEnv);
2033 }
2034
2035 /* Finish filling in the CachedPlanSource */
2036 CompleteCachedPlan(plansource,
2037 stmt_list,
2038 NULL,
2039 plan->argtypes,
2040 plan->nargs,
2041 plan->parserSetup,
2042 plan->parserSetupArg,
2043 plan->cursor_options,
2044 false); /* not fixed result */
2045 }
2046
2047 /*
2048 * Replan if needed, and increment plan refcount. If it's a saved
2049 * plan, the refcount must be backed by the CurrentResourceOwner.
2050 */
2051 cplan = GetCachedPlan(plansource, paramLI, plan->saved, _SPI_current->queryEnv);
2052 stmt_list = cplan->stmt_list;
2053
2054 /*
2055 * In the default non-read-only case, get a new snapshot, replacing
2056 * any that we pushed in a previous cycle.
2057 */
2058 if (snapshot == InvalidSnapshot && !read_only)
2059 {
2060 if (pushed_active_snap)
2061 PopActiveSnapshot();
2062 PushActiveSnapshot(GetTransactionSnapshot());
2063 pushed_active_snap = true;
2064 }
2065
2066 foreach(lc2, stmt_list)
2067 {
2068 PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
2069 bool canSetTag = stmt->canSetTag;
2070 DestReceiver *dest;
2071
2072 _SPI_current->processed = 0;
2073 _SPI_current->lastoid = InvalidOid;
2074 _SPI_current->tuptable = NULL;
2075
2076 if (stmt->utilityStmt)
2077 {
2078 if (IsA(stmt->utilityStmt, CopyStmt))
2079 {
2080 CopyStmt *cstmt = (CopyStmt *) stmt->utilityStmt;
2081
2082 if (cstmt->filename == NULL)
2083 {
2084 my_res = SPI_ERROR_COPY;
2085 goto fail;
2086 }
2087 }
2088 else if (IsA(stmt->utilityStmt, TransactionStmt))
2089 {
2090 my_res = SPI_ERROR_TRANSACTION;
2091 goto fail;
2092 }
2093 }
2094
2095 if (read_only && !CommandIsReadOnly(stmt))
2096 ereport(ERROR,
2097 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2098 /* translator: %s is a SQL statement name */
2099 errmsg("%s is not allowed in a non-volatile function",
2100 CreateCommandTag((Node *) stmt))));
2101
2102 if (IsInParallelMode() && !CommandIsReadOnly(stmt))
2103 PreventCommandIfParallelMode(CreateCommandTag((Node *) stmt));
2104
2105 /*
2106 * If not read-only mode, advance the command counter before each
2107 * command and update the snapshot.
2108 */
2109 if (!read_only)
2110 {
2111 CommandCounterIncrement();
2112 UpdateActiveSnapshotCommandId();
2113 }
2114
2115 dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
2116
2117 if (stmt->utilityStmt == NULL)
2118 {
2119 QueryDesc *qdesc;
2120 Snapshot snap;
2121
2122 if (ActiveSnapshotSet())
2123 snap = GetActiveSnapshot();
2124 else
2125 snap = InvalidSnapshot;
2126
2127 qdesc = CreateQueryDesc(stmt,
2128 plansource->query_string,
2129 snap, crosscheck_snapshot,
2130 dest,
2131 paramLI, _SPI_current->queryEnv,
2132 0);
2133 res = _SPI_pquery(qdesc, fire_triggers,
2134 canSetTag ? tcount : 0);
2135 FreeQueryDesc(qdesc);
2136 }
2137 else
2138 {
2139 char completionTag[COMPLETION_TAG_BUFSIZE];
2140
2141 ProcessUtility(stmt,
2142 plansource->query_string,
2143 PROCESS_UTILITY_QUERY,
2144 paramLI,
2145 _SPI_current->queryEnv,
2146 dest,
2147 completionTag);
2148
2149 /* Update "processed" if stmt returned tuples */
2150 if (_SPI_current->tuptable)
2151 _SPI_current->processed = _SPI_current->tuptable->alloced -
2152 _SPI_current->tuptable->free;
2153
2154 res = SPI_OK_UTILITY;
2155
2156 /*
2157 * Some utility statements return a row count, even though the
2158 * tuples are not returned to the caller.
2159 */
2160 if (IsA(stmt->utilityStmt, CreateTableAsStmt))
2161 {
2162 CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
2163
2164 if (strncmp(completionTag, "SELECT ", 7) == 0)
2165 _SPI_current->processed =
2166 pg_strtouint64(completionTag + 7, NULL, 10);
2167 else
2168 {
2169 /*
2170 * Must be an IF NOT EXISTS that did nothing, or a
2171 * CREATE ... WITH NO DATA.
2172 */
2173 Assert(ctastmt->if_not_exists ||
2174 ctastmt->into->skipData);
2175 _SPI_current->processed = 0;
2176 }
2177
2178 /*
2179 * For historical reasons, if CREATE TABLE AS was spelled
2180 * as SELECT INTO, return a special return code.
2181 */
2182 if (ctastmt->is_select_into)
2183 res = SPI_OK_SELINTO;
2184 }
2185 else if (IsA(stmt->utilityStmt, CopyStmt))
2186 {
2187 Assert(strncmp(completionTag, "COPY ", 5) == 0);
2188 _SPI_current->processed = pg_strtouint64(completionTag + 5,
2189 NULL, 10);
2190 }
2191 }
2192
2193 /*
2194 * The last canSetTag query sets the status values returned to the
2195 * caller. Be careful to free any tuptables not returned, to
2196 * avoid intratransaction memory leak.
2197 */
2198 if (canSetTag)
2199 {
2200 my_processed = _SPI_current->processed;
2201 my_lastoid = _SPI_current->lastoid;
2202 SPI_freetuptable(my_tuptable);
2203 my_tuptable = _SPI_current->tuptable;
2204 my_res = res;
2205 }
2206 else
2207 {
2208 SPI_freetuptable(_SPI_current->tuptable);
2209 _SPI_current->tuptable = NULL;
2210 }
2211 /* we know that the receiver doesn't need a destroy call */
2212 if (res < 0)
2213 {
2214 my_res = res;
2215 goto fail;
2216 }
2217 }
2218
2219 /* Done with this plan, so release refcount */
2220 ReleaseCachedPlan(cplan, plan->saved);
2221 cplan = NULL;
2222
2223 /*
2224 * If not read-only mode, advance the command counter after the last
2225 * command. This ensures that its effects are visible, in case it was
2226 * DDL that would affect the next CachedPlanSource.
2227 */
2228 if (!read_only)
2229 CommandCounterIncrement();
2230 }
2231
2232 fail:
2233
2234 /* Pop the snapshot off the stack if we pushed one */
2235 if (pushed_active_snap)
2236 PopActiveSnapshot();
2237
2238 /* We no longer need the cached plan refcount, if any */
2239 if (cplan)
2240 ReleaseCachedPlan(cplan, plan->saved);
2241
2242 /*
2243 * Pop the error context stack
2244 */
2245 error_context_stack = spierrcontext.previous;
2246
2247 /* Save results for caller */
2248 SPI_processed = my_processed;
2249 SPI_lastoid = my_lastoid;
2250 SPI_tuptable = my_tuptable;
2251
2252 /* tuptable now is caller's responsibility, not SPI's */
2253 _SPI_current->tuptable = NULL;
2254
2255 /*
2256 * If none of the queries had canSetTag, return SPI_OK_REWRITTEN. Prior to
2257 * 8.4, we used return the last query's result code, but not its auxiliary
2258 * results, but that's confusing.
2259 */
2260 if (my_res == 0)
2261 my_res = SPI_OK_REWRITTEN;
2262
2263 return my_res;
2264 }
2265
2266 /*
2267 * Convert arrays of query parameters to form wanted by planner and executor
2268 */
2269 static ParamListInfo
_SPI_convert_params(int nargs,Oid * argtypes,Datum * Values,const char * Nulls)2270 _SPI_convert_params(int nargs, Oid *argtypes,
2271 Datum *Values, const char *Nulls)
2272 {
2273 ParamListInfo paramLI;
2274
2275 if (nargs > 0)
2276 {
2277 int i;
2278
2279 paramLI = (ParamListInfo) palloc(offsetof(ParamListInfoData, params) +
2280 nargs * sizeof(ParamExternData));
2281 /* we have static list of params, so no hooks needed */
2282 paramLI->paramFetch = NULL;
2283 paramLI->paramFetchArg = NULL;
2284 paramLI->parserSetup = NULL;
2285 paramLI->parserSetupArg = NULL;
2286 paramLI->numParams = nargs;
2287 paramLI->paramMask = NULL;
2288
2289 for (i = 0; i < nargs; i++)
2290 {
2291 ParamExternData *prm = ¶mLI->params[i];
2292
2293 prm->value = Values[i];
2294 prm->isnull = (Nulls && Nulls[i] == 'n');
2295 prm->pflags = PARAM_FLAG_CONST;
2296 prm->ptype = argtypes[i];
2297 }
2298 }
2299 else
2300 paramLI = NULL;
2301 return paramLI;
2302 }
2303
2304 static int
_SPI_pquery(QueryDesc * queryDesc,bool fire_triggers,uint64 tcount)2305 _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
2306 {
2307 int operation = queryDesc->operation;
2308 int eflags;
2309 int res;
2310
2311 switch (operation)
2312 {
2313 case CMD_SELECT:
2314 if (queryDesc->dest->mydest != DestSPI)
2315 {
2316 /* Don't return SPI_OK_SELECT if we're discarding result */
2317 res = SPI_OK_UTILITY;
2318 }
2319 else
2320 res = SPI_OK_SELECT;
2321 break;
2322 case CMD_INSERT:
2323 if (queryDesc->plannedstmt->hasReturning)
2324 res = SPI_OK_INSERT_RETURNING;
2325 else
2326 res = SPI_OK_INSERT;
2327 break;
2328 case CMD_DELETE:
2329 if (queryDesc->plannedstmt->hasReturning)
2330 res = SPI_OK_DELETE_RETURNING;
2331 else
2332 res = SPI_OK_DELETE;
2333 break;
2334 case CMD_UPDATE:
2335 if (queryDesc->plannedstmt->hasReturning)
2336 res = SPI_OK_UPDATE_RETURNING;
2337 else
2338 res = SPI_OK_UPDATE;
2339 break;
2340 default:
2341 return SPI_ERROR_OPUNKNOWN;
2342 }
2343
2344 #ifdef SPI_EXECUTOR_STATS
2345 if (ShowExecutorStats)
2346 ResetUsage();
2347 #endif
2348
2349 /* Select execution options */
2350 if (fire_triggers)
2351 eflags = 0; /* default run-to-completion flags */
2352 else
2353 eflags = EXEC_FLAG_SKIP_TRIGGERS;
2354
2355 ExecutorStart(queryDesc, eflags);
2356
2357 ExecutorRun(queryDesc, ForwardScanDirection, tcount, true);
2358
2359 _SPI_current->processed = queryDesc->estate->es_processed;
2360 _SPI_current->lastoid = queryDesc->estate->es_lastoid;
2361
2362 if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
2363 queryDesc->dest->mydest == DestSPI)
2364 {
2365 if (_SPI_checktuples())
2366 elog(ERROR, "consistency check on SPI tuple count failed");
2367 }
2368
2369 ExecutorFinish(queryDesc);
2370 ExecutorEnd(queryDesc);
2371 /* FreeQueryDesc is done by the caller */
2372
2373 #ifdef SPI_EXECUTOR_STATS
2374 if (ShowExecutorStats)
2375 ShowUsage("SPI EXECUTOR STATS");
2376 #endif
2377
2378 return res;
2379 }
2380
2381 /*
2382 * _SPI_error_callback
2383 *
2384 * Add context information when a query invoked via SPI fails
2385 */
2386 static void
_SPI_error_callback(void * arg)2387 _SPI_error_callback(void *arg)
2388 {
2389 const char *query = (const char *) arg;
2390 int syntaxerrposition;
2391
2392 if (query == NULL) /* in case arg wasn't set yet */
2393 return;
2394
2395 /*
2396 * If there is a syntax error position, convert to internal syntax error;
2397 * otherwise treat the query as an item of context stack
2398 */
2399 syntaxerrposition = geterrposition();
2400 if (syntaxerrposition > 0)
2401 {
2402 errposition(0);
2403 internalerrposition(syntaxerrposition);
2404 internalerrquery(query);
2405 }
2406 else
2407 errcontext("SQL statement \"%s\"", query);
2408 }
2409
2410 /*
2411 * _SPI_cursor_operation()
2412 *
2413 * Do a FETCH or MOVE in a cursor
2414 */
2415 static void
_SPI_cursor_operation(Portal portal,FetchDirection direction,long count,DestReceiver * dest)2416 _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
2417 DestReceiver *dest)
2418 {
2419 uint64 nfetched;
2420
2421 /* Check that the portal is valid */
2422 if (!PortalIsValid(portal))
2423 elog(ERROR, "invalid portal in SPI cursor operation");
2424
2425 /* Push the SPI stack */
2426 if (_SPI_begin_call(true) < 0)
2427 elog(ERROR, "SPI cursor operation called while not connected");
2428
2429 /* Reset the SPI result (note we deliberately don't touch lastoid) */
2430 SPI_processed = 0;
2431 SPI_tuptable = NULL;
2432 _SPI_current->processed = 0;
2433 _SPI_current->tuptable = NULL;
2434
2435 /* Run the cursor */
2436 nfetched = PortalRunFetch(portal,
2437 direction,
2438 count,
2439 dest);
2440
2441 /*
2442 * Think not to combine this store with the preceding function call. If
2443 * the portal contains calls to functions that use SPI, then SPI_stack is
2444 * likely to move around while the portal runs. When control returns,
2445 * _SPI_current will point to the correct stack entry... but the pointer
2446 * may be different than it was beforehand. So we must be sure to re-fetch
2447 * the pointer after the function call completes.
2448 */
2449 _SPI_current->processed = nfetched;
2450
2451 if (dest->mydest == DestSPI && _SPI_checktuples())
2452 elog(ERROR, "consistency check on SPI tuple count failed");
2453
2454 /* Put the result into place for access by caller */
2455 SPI_processed = _SPI_current->processed;
2456 SPI_tuptable = _SPI_current->tuptable;
2457
2458 /* tuptable now is caller's responsibility, not SPI's */
2459 _SPI_current->tuptable = NULL;
2460
2461 /* Pop the SPI stack */
2462 _SPI_end_call(true);
2463 }
2464
2465
2466 static MemoryContext
_SPI_execmem(void)2467 _SPI_execmem(void)
2468 {
2469 return MemoryContextSwitchTo(_SPI_current->execCxt);
2470 }
2471
2472 static MemoryContext
_SPI_procmem(void)2473 _SPI_procmem(void)
2474 {
2475 return MemoryContextSwitchTo(_SPI_current->procCxt);
2476 }
2477
2478 /*
2479 * _SPI_begin_call: begin a SPI operation within a connected procedure
2480 *
2481 * use_exec is true if we intend to make use of the procedure's execCxt
2482 * during this SPI operation. We'll switch into that context, and arrange
2483 * for it to be cleaned up at _SPI_end_call or if an error occurs.
2484 */
2485 static int
_SPI_begin_call(bool use_exec)2486 _SPI_begin_call(bool use_exec)
2487 {
2488 if (_SPI_current == NULL)
2489 return SPI_ERROR_UNCONNECTED;
2490
2491 if (use_exec)
2492 {
2493 /* remember when the Executor operation started */
2494 _SPI_current->execSubid = GetCurrentSubTransactionId();
2495 /* switch to the Executor memory context */
2496 _SPI_execmem();
2497 }
2498
2499 return 0;
2500 }
2501
2502 /*
2503 * _SPI_end_call: end a SPI operation within a connected procedure
2504 *
2505 * use_exec must be the same as in the previous _SPI_begin_call
2506 *
2507 * Note: this currently has no failure return cases, so callers don't check
2508 */
2509 static int
_SPI_end_call(bool use_exec)2510 _SPI_end_call(bool use_exec)
2511 {
2512 if (use_exec)
2513 {
2514 /* switch to the procedure memory context */
2515 _SPI_procmem();
2516 /* mark Executor context no longer in use */
2517 _SPI_current->execSubid = InvalidSubTransactionId;
2518 /* and free Executor memory */
2519 MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
2520 }
2521
2522 return 0;
2523 }
2524
2525 static bool
_SPI_checktuples(void)2526 _SPI_checktuples(void)
2527 {
2528 uint64 processed = _SPI_current->processed;
2529 SPITupleTable *tuptable = _SPI_current->tuptable;
2530 bool failed = false;
2531
2532 if (tuptable == NULL) /* spi_dest_startup was not called */
2533 failed = true;
2534 else if (processed != (tuptable->alloced - tuptable->free))
2535 failed = true;
2536
2537 return failed;
2538 }
2539
2540 /*
2541 * Convert a "temporary" SPIPlan into an "unsaved" plan.
2542 *
2543 * The passed _SPI_plan struct is on the stack, and all its subsidiary data
2544 * is in or under the current SPI executor context. Copy the plan into the
2545 * SPI procedure context so it will survive _SPI_end_call(). To minimize
2546 * data copying, this destructively modifies the input plan, by taking the
2547 * plancache entries away from it and reparenting them to the new SPIPlan.
2548 */
2549 static SPIPlanPtr
_SPI_make_plan_non_temp(SPIPlanPtr plan)2550 _SPI_make_plan_non_temp(SPIPlanPtr plan)
2551 {
2552 SPIPlanPtr newplan;
2553 MemoryContext parentcxt = _SPI_current->procCxt;
2554 MemoryContext plancxt;
2555 MemoryContext oldcxt;
2556 ListCell *lc;
2557
2558 /* Assert the input is a temporary SPIPlan */
2559 Assert(plan->magic == _SPI_PLAN_MAGIC);
2560 Assert(plan->plancxt == NULL);
2561 /* One-shot plans can't be saved */
2562 Assert(!plan->oneshot);
2563
2564 /*
2565 * Create a memory context for the plan, underneath the procedure context.
2566 * We don't expect the plan to be very large.
2567 */
2568 plancxt = AllocSetContextCreate(parentcxt,
2569 "SPI Plan",
2570 ALLOCSET_SMALL_SIZES);
2571 oldcxt = MemoryContextSwitchTo(plancxt);
2572
2573 /* Copy the SPI_plan struct and subsidiary data into the new context */
2574 newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
2575 newplan->magic = _SPI_PLAN_MAGIC;
2576 newplan->saved = false;
2577 newplan->oneshot = false;
2578 newplan->plancache_list = NIL;
2579 newplan->plancxt = plancxt;
2580 newplan->cursor_options = plan->cursor_options;
2581 newplan->nargs = plan->nargs;
2582 if (plan->nargs > 0)
2583 {
2584 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
2585 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
2586 }
2587 else
2588 newplan->argtypes = NULL;
2589 newplan->parserSetup = plan->parserSetup;
2590 newplan->parserSetupArg = plan->parserSetupArg;
2591
2592 /*
2593 * Reparent all the CachedPlanSources into the procedure context. In
2594 * theory this could fail partway through due to the pallocs, but we don't
2595 * care too much since both the procedure context and the executor context
2596 * would go away on error.
2597 */
2598 foreach(lc, plan->plancache_list)
2599 {
2600 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
2601
2602 CachedPlanSetParentContext(plansource, parentcxt);
2603
2604 /* Build new list, with list cells in plancxt */
2605 newplan->plancache_list = lappend(newplan->plancache_list, plansource);
2606 }
2607
2608 MemoryContextSwitchTo(oldcxt);
2609
2610 /* For safety, unlink the CachedPlanSources from the temporary plan */
2611 plan->plancache_list = NIL;
2612
2613 return newplan;
2614 }
2615
2616 /*
2617 * Make a "saved" copy of the given plan.
2618 */
2619 static SPIPlanPtr
_SPI_save_plan(SPIPlanPtr plan)2620 _SPI_save_plan(SPIPlanPtr plan)
2621 {
2622 SPIPlanPtr newplan;
2623 MemoryContext plancxt;
2624 MemoryContext oldcxt;
2625 ListCell *lc;
2626
2627 /* One-shot plans can't be saved */
2628 Assert(!plan->oneshot);
2629
2630 /*
2631 * Create a memory context for the plan. We don't expect the plan to be
2632 * very large, so use smaller-than-default alloc parameters. It's a
2633 * transient context until we finish copying everything.
2634 */
2635 plancxt = AllocSetContextCreate(CurrentMemoryContext,
2636 "SPI Plan",
2637 ALLOCSET_SMALL_SIZES);
2638 oldcxt = MemoryContextSwitchTo(plancxt);
2639
2640 /* Copy the SPI plan into its own context */
2641 newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
2642 newplan->magic = _SPI_PLAN_MAGIC;
2643 newplan->saved = false;
2644 newplan->oneshot = false;
2645 newplan->plancache_list = NIL;
2646 newplan->plancxt = plancxt;
2647 newplan->cursor_options = plan->cursor_options;
2648 newplan->nargs = plan->nargs;
2649 if (plan->nargs > 0)
2650 {
2651 newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
2652 memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
2653 }
2654 else
2655 newplan->argtypes = NULL;
2656 newplan->parserSetup = plan->parserSetup;
2657 newplan->parserSetupArg = plan->parserSetupArg;
2658
2659 /* Copy all the plancache entries */
2660 foreach(lc, plan->plancache_list)
2661 {
2662 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
2663 CachedPlanSource *newsource;
2664
2665 newsource = CopyCachedPlan(plansource);
2666 newplan->plancache_list = lappend(newplan->plancache_list, newsource);
2667 }
2668
2669 MemoryContextSwitchTo(oldcxt);
2670
2671 /*
2672 * Mark it saved, reparent it under CacheMemoryContext, and mark all the
2673 * component CachedPlanSources as saved. This sequence cannot fail
2674 * partway through, so there's no risk of long-term memory leakage.
2675 */
2676 newplan->saved = true;
2677 MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
2678
2679 foreach(lc, newplan->plancache_list)
2680 {
2681 CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
2682
2683 SaveCachedPlan(plansource);
2684 }
2685
2686 return newplan;
2687 }
2688
2689 /*
2690 * Internal lookup of ephemeral named relation by name.
2691 */
2692 static EphemeralNamedRelation
_SPI_find_ENR_by_name(const char * name)2693 _SPI_find_ENR_by_name(const char *name)
2694 {
2695 /* internal static function; any error is bug in SPI itself */
2696 Assert(name != NULL);
2697
2698 /* fast exit if no tuplestores have been added */
2699 if (_SPI_current->queryEnv == NULL)
2700 return NULL;
2701
2702 return get_ENR(_SPI_current->queryEnv, name);
2703 }
2704
2705 /*
2706 * Register an ephemeral named relation for use by the planner and executor on
2707 * subsequent calls using this SPI connection.
2708 */
2709 int
SPI_register_relation(EphemeralNamedRelation enr)2710 SPI_register_relation(EphemeralNamedRelation enr)
2711 {
2712 EphemeralNamedRelation match;
2713 int res;
2714
2715 if (enr == NULL || enr->md.name == NULL)
2716 return SPI_ERROR_ARGUMENT;
2717
2718 res = _SPI_begin_call(false); /* keep current memory context */
2719 if (res < 0)
2720 return res;
2721
2722 match = _SPI_find_ENR_by_name(enr->md.name);
2723 if (match)
2724 res = SPI_ERROR_REL_DUPLICATE;
2725 else
2726 {
2727 if (_SPI_current->queryEnv == NULL)
2728 _SPI_current->queryEnv = create_queryEnv();
2729
2730 register_ENR(_SPI_current->queryEnv, enr);
2731 res = SPI_OK_REL_REGISTER;
2732 }
2733
2734 _SPI_end_call(false);
2735
2736 return res;
2737 }
2738
2739 /*
2740 * Unregister an ephemeral named relation by name. This will probably be a
2741 * rarely used function, since SPI_finish will clear it automatically.
2742 */
2743 int
SPI_unregister_relation(const char * name)2744 SPI_unregister_relation(const char *name)
2745 {
2746 EphemeralNamedRelation match;
2747 int res;
2748
2749 if (name == NULL)
2750 return SPI_ERROR_ARGUMENT;
2751
2752 res = _SPI_begin_call(false); /* keep current memory context */
2753 if (res < 0)
2754 return res;
2755
2756 match = _SPI_find_ENR_by_name(name);
2757 if (match)
2758 {
2759 unregister_ENR(_SPI_current->queryEnv, match->md.name);
2760 res = SPI_OK_REL_UNREGISTER;
2761 }
2762 else
2763 res = SPI_ERROR_REL_NOT_FOUND;
2764
2765 _SPI_end_call(false);
2766
2767 return res;
2768 }
2769
2770 /*
2771 * Register the transient relations from 'tdata' using this SPI connection.
2772 * This should be called by PL implementations' trigger handlers after
2773 * connecting, in order to make transition tables visible to any queries run
2774 * in this connection.
2775 */
2776 int
SPI_register_trigger_data(TriggerData * tdata)2777 SPI_register_trigger_data(TriggerData *tdata)
2778 {
2779 if (tdata == NULL)
2780 return SPI_ERROR_ARGUMENT;
2781
2782 if (tdata->tg_newtable)
2783 {
2784 EphemeralNamedRelation enr =
2785 palloc(sizeof(EphemeralNamedRelationData));
2786 int rc;
2787
2788 enr->md.name = tdata->tg_trigger->tgnewtable;
2789 enr->md.reliddesc = tdata->tg_relation->rd_id;
2790 enr->md.tupdesc = NULL;
2791 enr->md.enrtype = ENR_NAMED_TUPLESTORE;
2792 enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_newtable);
2793 enr->reldata = tdata->tg_newtable;
2794 rc = SPI_register_relation(enr);
2795 if (rc != SPI_OK_REL_REGISTER)
2796 return rc;
2797 }
2798
2799 if (tdata->tg_oldtable)
2800 {
2801 EphemeralNamedRelation enr =
2802 palloc(sizeof(EphemeralNamedRelationData));
2803 int rc;
2804
2805 enr->md.name = tdata->tg_trigger->tgoldtable;
2806 enr->md.reliddesc = tdata->tg_relation->rd_id;
2807 enr->md.tupdesc = NULL;
2808 enr->md.enrtype = ENR_NAMED_TUPLESTORE;
2809 enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_oldtable);
2810 enr->reldata = tdata->tg_oldtable;
2811 rc = SPI_register_relation(enr);
2812 if (rc != SPI_OK_REL_REGISTER)
2813 return rc;
2814 }
2815
2816 return SPI_OK_TD_REGISTER;
2817 }
2818