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