Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * spi.c
4 : * Server Programming Interface
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 "tcop/pquery.h"
27 : #include "tcop/utility.h"
28 : #include "utils/builtins.h"
29 : #include "utils/datum.h"
30 : #include "utils/lsyscache.h"
31 : #include "utils/memutils.h"
32 : #include "utils/rel.h"
33 : #include "utils/snapmgr.h"
34 : #include "utils/syscache.h"
35 : #include "utils/tuplestore.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 : SPITupleTable *SPI_tuptable = NULL;
47 : int SPI_result = 0;
48 :
49 : static _SPI_connection *_SPI_stack = NULL;
50 : static _SPI_connection *_SPI_current = NULL;
51 : static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
52 : static int _SPI_connected = -1; /* current stack index */
53 :
54 : typedef struct SPICallbackArg
55 : {
56 : const char *query;
57 : RawParseMode mode;
58 : } SPICallbackArg;
59 :
60 : static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
61 : ParamListInfo paramLI, bool read_only);
62 :
63 : static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
64 :
65 : static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
66 :
67 : static int _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
68 : Snapshot snapshot, Snapshot crosscheck_snapshot,
69 : bool fire_triggers);
70 :
71 : static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
72 : const Datum *Values, const char *Nulls);
73 :
74 : static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount);
75 :
76 : static void _SPI_error_callback(void *arg);
77 :
78 : static void _SPI_cursor_operation(Portal portal,
79 : FetchDirection direction, long count,
80 : DestReceiver *dest);
81 :
82 : static SPIPlanPtr _SPI_make_plan_non_temp(SPIPlanPtr plan);
83 : static SPIPlanPtr _SPI_save_plan(SPIPlanPtr plan);
84 :
85 : static int _SPI_begin_call(bool use_exec);
86 : static int _SPI_end_call(bool use_exec);
87 : static MemoryContext _SPI_execmem(void);
88 : static MemoryContext _SPI_procmem(void);
89 : static bool _SPI_checktuples(void);
90 :
91 :
92 : /* =================== interface functions =================== */
93 :
94 : int
95 410817 : SPI_connect(void)
96 : {
97 410817 : return SPI_connect_ext(0);
98 : }
99 :
100 : int
101 466377 : SPI_connect_ext(int options)
102 : {
103 : int newdepth;
104 :
105 : /* Enlarge stack if necessary */
106 466377 : if (_SPI_stack == NULL)
107 : {
108 1101 : if (_SPI_connected != -1 || _SPI_stack_depth != 0)
109 0 : elog(ERROR, "SPI stack corrupted");
110 1101 : newdepth = 16;
111 1101 : _SPI_stack = (_SPI_connection *)
112 1101 : MemoryContextAlloc(TopMemoryContext,
113 : newdepth * sizeof(_SPI_connection));
114 1101 : _SPI_stack_depth = newdepth;
115 : }
116 : else
117 : {
118 465276 : if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
119 0 : elog(ERROR, "SPI stack corrupted");
120 465276 : if (_SPI_stack_depth == _SPI_connected + 1)
121 : {
122 13 : newdepth = _SPI_stack_depth * 2;
123 13 : _SPI_stack = (_SPI_connection *)
124 13 : repalloc(_SPI_stack,
125 : newdepth * sizeof(_SPI_connection));
126 13 : _SPI_stack_depth = newdepth;
127 : }
128 : }
129 :
130 : /* Enter new stack level */
131 466377 : _SPI_connected++;
132 : Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
133 :
134 466377 : _SPI_current = &(_SPI_stack[_SPI_connected]);
135 466377 : _SPI_current->processed = 0;
136 466377 : _SPI_current->tuptable = NULL;
137 466377 : _SPI_current->execSubid = InvalidSubTransactionId;
138 466377 : slist_init(&_SPI_current->tuptables);
139 466377 : _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
140 466377 : _SPI_current->execCxt = NULL;
141 466377 : _SPI_current->connectSubid = GetCurrentSubTransactionId();
142 466377 : _SPI_current->queryEnv = NULL;
143 466377 : _SPI_current->atomic = (options & SPI_OPT_NONATOMIC ? false : true);
144 466377 : _SPI_current->internal_xact = false;
145 466377 : _SPI_current->outer_processed = SPI_processed;
146 466377 : _SPI_current->outer_tuptable = SPI_tuptable;
147 466377 : _SPI_current->outer_result = SPI_result;
148 :
149 : /*
150 : * Create memory contexts for this procedure
151 : *
152 : * In atomic contexts (the normal case), we use TopTransactionContext,
153 : * otherwise PortalContext, so that it lives across transaction
154 : * boundaries.
155 : *
156 : * XXX It could be better to use PortalContext as the parent context in
157 : * all cases, but we may not be inside a portal (consider deferred-trigger
158 : * execution). Perhaps CurTransactionContext could be an option? For now
159 : * it doesn't matter because we clean up explicitly in AtEOSubXact_SPI();
160 : * but see also AtEOXact_SPI().
161 : */
162 466377 : _SPI_current->procCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : PortalContext,
163 : "SPI Proc",
164 : ALLOCSET_DEFAULT_SIZES);
165 466377 : _SPI_current->execCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : _SPI_current->procCxt,
166 : "SPI Exec",
167 : ALLOCSET_DEFAULT_SIZES);
168 : /* ... and switch to procedure's context */
169 466377 : _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
170 :
171 : /*
172 : * Reset API global variables so that current caller cannot accidentally
173 : * depend on state of an outer caller.
174 : */
175 466377 : SPI_processed = 0;
176 466377 : SPI_tuptable = NULL;
177 466377 : SPI_result = 0;
178 :
179 466377 : return SPI_OK_CONNECT;
180 : }
181 :
182 : int
183 464577 : SPI_finish(void)
184 : {
185 : int res;
186 :
187 464577 : res = _SPI_begin_call(false); /* just check we're connected */
188 464577 : if (res < 0)
189 0 : return res;
190 :
191 : /* Restore memory context as it was before procedure call */
192 464577 : MemoryContextSwitchTo(_SPI_current->savedcxt);
193 :
194 : /* Release memory used in procedure call (including tuptables) */
195 464577 : MemoryContextDelete(_SPI_current->execCxt);
196 464577 : _SPI_current->execCxt = NULL;
197 464577 : MemoryContextDelete(_SPI_current->procCxt);
198 464577 : _SPI_current->procCxt = NULL;
199 :
200 : /*
201 : * Restore outer API variables, especially SPI_tuptable which is probably
202 : * pointing at a just-deleted tuptable
203 : */
204 464577 : SPI_processed = _SPI_current->outer_processed;
205 464577 : SPI_tuptable = _SPI_current->outer_tuptable;
206 464577 : SPI_result = _SPI_current->outer_result;
207 :
208 : /* Exit stack level */
209 464577 : _SPI_connected--;
210 464577 : if (_SPI_connected < 0)
211 456301 : _SPI_current = NULL;
212 : else
213 8276 : _SPI_current = &(_SPI_stack[_SPI_connected]);
214 :
215 464577 : return SPI_OK_FINISH;
216 : }
217 :
218 : /*
219 : * SPI_start_transaction is a no-op, kept for backwards compatibility.
220 : * SPI callers are *always* inside a transaction.
221 : */
222 : void
223 0 : SPI_start_transaction(void)
224 : {
225 0 : }
226 :
227 : static void
228 2144 : _SPI_commit(bool chain)
229 : {
230 2144 : MemoryContext oldcontext = CurrentMemoryContext;
231 : SavedTransactionCharacteristics savetc;
232 :
233 : /*
234 : * Complain if we are in a context that doesn't permit transaction
235 : * termination. (Note: here and _SPI_rollback should be the only places
236 : * that throw ERRCODE_INVALID_TRANSACTION_TERMINATION, so that callers can
237 : * test for that with security that they know what happened.)
238 : */
239 2144 : if (_SPI_current->atomic)
240 16 : ereport(ERROR,
241 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
242 : errmsg("invalid transaction termination")));
243 :
244 : /*
245 : * This restriction is required by PLs implemented on top of SPI. They
246 : * use subtransactions to establish exception blocks that are supposed to
247 : * be rolled back together if there is an error. Terminating the
248 : * top-level transaction in such a block violates that idea. A future PL
249 : * implementation might have different ideas about this, in which case
250 : * this restriction would have to be refined or the check possibly be
251 : * moved out of SPI into the PLs. Note however that the code below relies
252 : * on not being within a subtransaction.
253 : */
254 2128 : if (IsSubTransaction())
255 3 : ereport(ERROR,
256 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
257 : errmsg("cannot commit while a subtransaction is active")));
258 :
259 2125 : if (chain)
260 2 : SaveTransactionCharacteristics(&savetc);
261 :
262 : /* Catch any error occurring during the COMMIT */
263 2125 : PG_TRY();
264 : {
265 : /* Protect current SPI stack entry against deletion */
266 2125 : _SPI_current->internal_xact = true;
267 :
268 : /*
269 : * Hold any pinned portals that any PLs might be using. We have to do
270 : * this before changing transaction state, since this will run
271 : * user-defined code that might throw an error.
272 : */
273 2125 : HoldPinnedPortals();
274 :
275 : /* Release snapshots associated with portals */
276 2124 : ForgetPortalSnapshots();
277 :
278 : /* Do the deed */
279 2124 : CommitTransactionCommand();
280 :
281 : /* Immediately start a new transaction */
282 2117 : StartTransactionCommand();
283 2117 : if (chain)
284 2 : RestoreTransactionCharacteristics(&savetc);
285 :
286 2117 : MemoryContextSwitchTo(oldcontext);
287 :
288 2117 : _SPI_current->internal_xact = false;
289 : }
290 8 : PG_CATCH();
291 : {
292 : ErrorData *edata;
293 :
294 : /* Save error info in caller's context */
295 8 : MemoryContextSwitchTo(oldcontext);
296 8 : edata = CopyErrorData();
297 8 : FlushErrorState();
298 :
299 : /*
300 : * Abort the failed transaction. If this fails too, we'll just
301 : * propagate the error out ... there's not that much we can do.
302 : */
303 8 : AbortCurrentTransaction();
304 :
305 : /* ... and start a new one */
306 8 : StartTransactionCommand();
307 8 : if (chain)
308 0 : RestoreTransactionCharacteristics(&savetc);
309 :
310 8 : MemoryContextSwitchTo(oldcontext);
311 :
312 8 : _SPI_current->internal_xact = false;
313 :
314 : /* Now that we've cleaned up the transaction, re-throw the error */
315 8 : ReThrowError(edata);
316 : }
317 2117 : PG_END_TRY();
318 2117 : }
319 :
320 : void
321 2142 : SPI_commit(void)
322 : {
323 2142 : _SPI_commit(false);
324 2115 : }
325 :
326 : void
327 2 : SPI_commit_and_chain(void)
328 : {
329 2 : _SPI_commit(true);
330 2 : }
331 :
332 : static void
333 93 : _SPI_rollback(bool chain)
334 : {
335 93 : MemoryContext oldcontext = CurrentMemoryContext;
336 : SavedTransactionCharacteristics savetc;
337 :
338 : /* see comments in _SPI_commit() */
339 93 : if (_SPI_current->atomic)
340 0 : ereport(ERROR,
341 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
342 : errmsg("invalid transaction termination")));
343 :
344 : /* see comments in _SPI_commit() */
345 93 : if (IsSubTransaction())
346 2 : ereport(ERROR,
347 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
348 : errmsg("cannot roll back while a subtransaction is active")));
349 :
350 91 : if (chain)
351 2 : SaveTransactionCharacteristics(&savetc);
352 :
353 : /* Catch any error occurring during the ROLLBACK */
354 91 : PG_TRY();
355 : {
356 : /* Protect current SPI stack entry against deletion */
357 91 : _SPI_current->internal_xact = true;
358 :
359 : /*
360 : * Hold any pinned portals that any PLs might be using. We have to do
361 : * this before changing transaction state, since this will run
362 : * user-defined code that might throw an error, and in any case
363 : * couldn't be run in an already-aborted transaction.
364 : */
365 91 : HoldPinnedPortals();
366 :
367 : /* Release snapshots associated with portals */
368 89 : ForgetPortalSnapshots();
369 :
370 : /* Do the deed */
371 89 : AbortCurrentTransaction();
372 :
373 : /* Immediately start a new transaction */
374 89 : StartTransactionCommand();
375 89 : if (chain)
376 2 : RestoreTransactionCharacteristics(&savetc);
377 :
378 89 : MemoryContextSwitchTo(oldcontext);
379 :
380 89 : _SPI_current->internal_xact = false;
381 : }
382 2 : PG_CATCH();
383 : {
384 : ErrorData *edata;
385 :
386 : /* Save error info in caller's context */
387 2 : MemoryContextSwitchTo(oldcontext);
388 2 : edata = CopyErrorData();
389 2 : FlushErrorState();
390 :
391 : /*
392 : * Try again to abort the failed transaction. If this fails too,
393 : * we'll just propagate the error out ... there's not that much we can
394 : * do.
395 : */
396 2 : AbortCurrentTransaction();
397 :
398 : /* ... and start a new one */
399 2 : StartTransactionCommand();
400 2 : if (chain)
401 0 : RestoreTransactionCharacteristics(&savetc);
402 :
403 2 : MemoryContextSwitchTo(oldcontext);
404 :
405 2 : _SPI_current->internal_xact = false;
406 :
407 : /* Now that we've cleaned up the transaction, re-throw the error */
408 2 : ReThrowError(edata);
409 : }
410 89 : PG_END_TRY();
411 89 : }
412 :
413 : void
414 91 : SPI_rollback(void)
415 : {
416 91 : _SPI_rollback(false);
417 87 : }
418 :
419 : void
420 2 : SPI_rollback_and_chain(void)
421 : {
422 2 : _SPI_rollback(true);
423 2 : }
424 :
425 : /*
426 : * Clean up SPI state at transaction commit or abort.
427 : */
428 : void
429 609081 : AtEOXact_SPI(bool isCommit)
430 : {
431 609081 : bool found = false;
432 :
433 : /*
434 : * Pop stack entries, stopping if we find one marked internal_xact (that
435 : * one belongs to the caller of SPI_commit or SPI_rollback).
436 : */
437 610741 : while (_SPI_connected >= 0)
438 : {
439 3876 : _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
440 :
441 3876 : if (connection->internal_xact)
442 2216 : break;
443 :
444 1660 : found = true;
445 :
446 : /*
447 : * We need not release the procedure's memory contexts explicitly, as
448 : * they'll go away automatically when their parent context does; see
449 : * notes in SPI_connect_ext.
450 : */
451 :
452 : /*
453 : * Restore outer global variables and pop the stack entry. Unlike
454 : * SPI_finish(), we don't risk switching to memory contexts that might
455 : * be already gone.
456 : */
457 1660 : SPI_processed = connection->outer_processed;
458 1660 : SPI_tuptable = connection->outer_tuptable;
459 1660 : SPI_result = connection->outer_result;
460 :
461 1660 : _SPI_connected--;
462 1660 : if (_SPI_connected < 0)
463 1621 : _SPI_current = NULL;
464 : else
465 39 : _SPI_current = &(_SPI_stack[_SPI_connected]);
466 : }
467 :
468 : /* We should only find entries to pop during an ABORT. */
469 609081 : if (found && isCommit)
470 0 : ereport(WARNING,
471 : (errcode(ERRCODE_WARNING),
472 : errmsg("transaction left non-empty SPI stack"),
473 : errhint("Check for missing \"SPI_finish\" calls.")));
474 609081 : }
475 :
476 : /*
477 : * Clean up SPI state at subtransaction commit or abort.
478 : *
479 : * During commit, there shouldn't be any unclosed entries remaining from
480 : * the current subtransaction; we emit a warning if any are found.
481 : */
482 : void
483 11790 : AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
484 : {
485 11790 : bool found = false;
486 :
487 11930 : while (_SPI_connected >= 0)
488 : {
489 9397 : _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
490 :
491 9397 : if (connection->connectSubid != mySubid)
492 9257 : break; /* couldn't be any underneath it either */
493 :
494 140 : if (connection->internal_xact)
495 0 : break;
496 :
497 140 : found = true;
498 :
499 : /*
500 : * Release procedure memory explicitly (see note in SPI_connect)
501 : */
502 140 : if (connection->execCxt)
503 : {
504 140 : MemoryContextDelete(connection->execCxt);
505 140 : connection->execCxt = NULL;
506 : }
507 140 : if (connection->procCxt)
508 : {
509 140 : MemoryContextDelete(connection->procCxt);
510 140 : connection->procCxt = NULL;
511 : }
512 :
513 : /*
514 : * Restore outer global variables and pop the stack entry. Unlike
515 : * SPI_finish(), we don't risk switching to memory contexts that might
516 : * be already gone.
517 : */
518 140 : SPI_processed = connection->outer_processed;
519 140 : SPI_tuptable = connection->outer_tuptable;
520 140 : SPI_result = connection->outer_result;
521 :
522 140 : _SPI_connected--;
523 140 : if (_SPI_connected < 0)
524 49 : _SPI_current = NULL;
525 : else
526 91 : _SPI_current = &(_SPI_stack[_SPI_connected]);
527 : }
528 :
529 11790 : if (found && isCommit)
530 0 : ereport(WARNING,
531 : (errcode(ERRCODE_WARNING),
532 : errmsg("subtransaction left non-empty SPI stack"),
533 : errhint("Check for missing \"SPI_finish\" calls.")));
534 :
535 : /*
536 : * If we are aborting a subtransaction and there is an open SPI context
537 : * surrounding the subxact, clean up to prevent memory leakage.
538 : */
539 11790 : if (_SPI_current && !isCommit)
540 : {
541 : slist_mutable_iter siter;
542 :
543 : /*
544 : * Throw away executor state if current executor operation was started
545 : * within current subxact (essentially, force a _SPI_end_call(true)).
546 : */
547 4259 : if (_SPI_current->execSubid >= mySubid)
548 : {
549 3744 : _SPI_current->execSubid = InvalidSubTransactionId;
550 3744 : MemoryContextReset(_SPI_current->execCxt);
551 : }
552 :
553 : /* throw away any tuple tables created within current subxact */
554 10417 : slist_foreach_modify(siter, &_SPI_current->tuptables)
555 : {
556 : SPITupleTable *tuptable;
557 :
558 6158 : tuptable = slist_container(SPITupleTable, next, siter.cur);
559 6158 : if (tuptable->subid >= mySubid)
560 : {
561 : /*
562 : * If we used SPI_freetuptable() here, its internal search of
563 : * the tuptables list would make this operation O(N^2).
564 : * Instead, just free the tuptable manually. This should
565 : * match what SPI_freetuptable() does.
566 : */
567 3590 : slist_delete_current(&siter);
568 3590 : if (tuptable == _SPI_current->tuptable)
569 3586 : _SPI_current->tuptable = NULL;
570 3590 : if (tuptable == SPI_tuptable)
571 4 : SPI_tuptable = NULL;
572 3590 : MemoryContextDelete(tuptable->tuptabcxt);
573 : }
574 : }
575 : }
576 11790 : }
577 :
578 : /*
579 : * Are we executing inside a procedure (that is, a nonatomic SPI context)?
580 : */
581 : bool
582 603117 : SPI_inside_nonatomic_context(void)
583 : {
584 603117 : if (_SPI_current == NULL)
585 600901 : return false; /* not in any SPI context at all */
586 : /* these tests must match _SPI_commit's opinion of what's atomic: */
587 2216 : if (_SPI_current->atomic)
588 0 : return false; /* it's atomic (ie function not procedure) */
589 2216 : if (IsSubTransaction())
590 0 : return false; /* if within subtransaction, it's atomic */
591 2216 : return true;
592 : }
593 :
594 :
595 : /* Parse, plan, and execute a query string */
596 : int
597 854 : SPI_execute(const char *src, bool read_only, long tcount)
598 : {
599 : _SPI_plan plan;
600 : SPIExecuteOptions options;
601 : int res;
602 :
603 854 : if (src == NULL || tcount < 0)
604 0 : return SPI_ERROR_ARGUMENT;
605 :
606 854 : res = _SPI_begin_call(true);
607 854 : if (res < 0)
608 0 : return res;
609 :
610 854 : memset(&plan, 0, sizeof(_SPI_plan));
611 854 : plan.magic = _SPI_PLAN_MAGIC;
612 854 : plan.parse_mode = RAW_PARSE_DEFAULT;
613 854 : plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
614 :
615 854 : _SPI_prepare_oneshot_plan(src, &plan);
616 :
617 846 : memset(&options, 0, sizeof(options));
618 846 : options.read_only = read_only;
619 846 : options.tcount = tcount;
620 :
621 846 : res = _SPI_execute_plan(&plan, &options,
622 : InvalidSnapshot, InvalidSnapshot,
623 : true);
624 :
625 819 : _SPI_end_call(true);
626 819 : return res;
627 : }
628 :
629 : /* Obsolete version of SPI_execute */
630 : int
631 325 : SPI_exec(const char *src, long tcount)
632 : {
633 325 : return SPI_execute(src, false, tcount);
634 : }
635 :
636 : /* Parse, plan, and execute a query string, with extensible options */
637 : int
638 10771 : SPI_execute_extended(const char *src,
639 : const SPIExecuteOptions *options)
640 : {
641 : int res;
642 : _SPI_plan plan;
643 :
644 10771 : if (src == NULL || options == NULL)
645 0 : return SPI_ERROR_ARGUMENT;
646 :
647 10771 : res = _SPI_begin_call(true);
648 10771 : if (res < 0)
649 0 : return res;
650 :
651 10771 : memset(&plan, 0, sizeof(_SPI_plan));
652 10771 : plan.magic = _SPI_PLAN_MAGIC;
653 10771 : plan.parse_mode = RAW_PARSE_DEFAULT;
654 10771 : plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
655 10771 : if (options->params)
656 : {
657 289 : plan.parserSetup = options->params->parserSetup;
658 289 : plan.parserSetupArg = options->params->parserSetupArg;
659 : }
660 :
661 10771 : _SPI_prepare_oneshot_plan(src, &plan);
662 :
663 10771 : res = _SPI_execute_plan(&plan, options,
664 : InvalidSnapshot, InvalidSnapshot,
665 : true);
666 :
667 10614 : _SPI_end_call(true);
668 10614 : return res;
669 : }
670 :
671 : /* Execute a previously prepared plan */
672 : int
673 2585 : SPI_execute_plan(SPIPlanPtr plan, const Datum *Values, const char *Nulls,
674 : bool read_only, long tcount)
675 : {
676 : SPIExecuteOptions options;
677 : int res;
678 :
679 2585 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
680 0 : return SPI_ERROR_ARGUMENT;
681 :
682 2585 : if (plan->nargs > 0 && Values == NULL)
683 0 : return SPI_ERROR_PARAM;
684 :
685 2585 : res = _SPI_begin_call(true);
686 2585 : if (res < 0)
687 0 : return res;
688 :
689 2585 : memset(&options, 0, sizeof(options));
690 2585 : options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
691 : Values, Nulls);
692 2585 : options.read_only = read_only;
693 2585 : options.tcount = tcount;
694 :
695 2585 : res = _SPI_execute_plan(plan, &options,
696 : InvalidSnapshot, InvalidSnapshot,
697 : true);
698 :
699 2584 : _SPI_end_call(true);
700 2584 : return res;
701 : }
702 :
703 : /* Obsolete version of SPI_execute_plan */
704 : int
705 27 : SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
706 : {
707 27 : return SPI_execute_plan(plan, Values, Nulls, false, tcount);
708 : }
709 :
710 : /* Execute a previously prepared plan */
711 : int
712 1682 : SPI_execute_plan_extended(SPIPlanPtr plan,
713 : const SPIExecuteOptions *options)
714 : {
715 : int res;
716 :
717 1682 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || options == NULL)
718 0 : return SPI_ERROR_ARGUMENT;
719 :
720 1682 : res = _SPI_begin_call(true);
721 1682 : if (res < 0)
722 0 : return res;
723 :
724 1682 : res = _SPI_execute_plan(plan, options,
725 : InvalidSnapshot, InvalidSnapshot,
726 : true);
727 :
728 1676 : _SPI_end_call(true);
729 1676 : return res;
730 : }
731 :
732 : /* Execute a previously prepared plan */
733 : int
734 48196 : SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
735 : bool read_only, long tcount)
736 : {
737 : SPIExecuteOptions options;
738 : int res;
739 :
740 48196 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
741 0 : return SPI_ERROR_ARGUMENT;
742 :
743 48196 : res = _SPI_begin_call(true);
744 48196 : if (res < 0)
745 0 : return res;
746 :
747 48196 : memset(&options, 0, sizeof(options));
748 48196 : options.params = params;
749 48196 : options.read_only = read_only;
750 48196 : options.tcount = tcount;
751 :
752 48196 : res = _SPI_execute_plan(plan, &options,
753 : InvalidSnapshot, InvalidSnapshot,
754 : true);
755 :
756 44568 : _SPI_end_call(true);
757 44568 : return res;
758 : }
759 :
760 : /*
761 : * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
762 : * the caller to specify exactly which snapshots to use, which will be
763 : * registered here. Also, the caller may specify that AFTER triggers should be
764 : * queued as part of the outer query rather than being fired immediately at the
765 : * end of the command.
766 : *
767 : * This is currently not documented in spi.sgml because it is only intended
768 : * for use by RI triggers.
769 : *
770 : * Passing snapshot == InvalidSnapshot will select the normal behavior of
771 : * fetching a new snapshot for each query.
772 : */
773 : int
774 405126 : SPI_execute_snapshot(SPIPlanPtr plan,
775 : const Datum *Values, const char *Nulls,
776 : Snapshot snapshot, Snapshot crosscheck_snapshot,
777 : bool read_only, bool fire_triggers, long tcount)
778 : {
779 : SPIExecuteOptions options;
780 : int res;
781 :
782 405126 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
783 0 : return SPI_ERROR_ARGUMENT;
784 :
785 405126 : if (plan->nargs > 0 && Values == NULL)
786 0 : return SPI_ERROR_PARAM;
787 :
788 405126 : res = _SPI_begin_call(true);
789 405126 : if (res < 0)
790 0 : return res;
791 :
792 405126 : memset(&options, 0, sizeof(options));
793 405126 : options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
794 : Values, Nulls);
795 405126 : options.read_only = read_only;
796 405126 : options.tcount = tcount;
797 :
798 405126 : res = _SPI_execute_plan(plan, &options,
799 : snapshot, crosscheck_snapshot,
800 : fire_triggers);
801 :
802 405111 : _SPI_end_call(true);
803 405111 : return res;
804 : }
805 :
806 : /*
807 : * SPI_execute_with_args -- plan and execute a query with supplied arguments
808 : *
809 : * This is functionally equivalent to SPI_prepare followed by
810 : * SPI_execute_plan.
811 : */
812 : int
813 0 : SPI_execute_with_args(const char *src,
814 : int nargs, Oid *argtypes,
815 : const Datum *Values, const char *Nulls,
816 : bool read_only, long tcount)
817 : {
818 : int res;
819 : _SPI_plan plan;
820 : ParamListInfo paramLI;
821 : SPIExecuteOptions options;
822 :
823 0 : if (src == NULL || nargs < 0 || tcount < 0)
824 0 : return SPI_ERROR_ARGUMENT;
825 :
826 0 : if (nargs > 0 && (argtypes == NULL || Values == NULL))
827 0 : return SPI_ERROR_PARAM;
828 :
829 0 : res = _SPI_begin_call(true);
830 0 : if (res < 0)
831 0 : return res;
832 :
833 0 : memset(&plan, 0, sizeof(_SPI_plan));
834 0 : plan.magic = _SPI_PLAN_MAGIC;
835 0 : plan.parse_mode = RAW_PARSE_DEFAULT;
836 0 : plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
837 0 : plan.nargs = nargs;
838 0 : plan.argtypes = argtypes;
839 0 : plan.parserSetup = NULL;
840 0 : plan.parserSetupArg = NULL;
841 :
842 0 : paramLI = _SPI_convert_params(nargs, argtypes,
843 : Values, Nulls);
844 :
845 0 : _SPI_prepare_oneshot_plan(src, &plan);
846 :
847 0 : memset(&options, 0, sizeof(options));
848 0 : options.params = paramLI;
849 0 : options.read_only = read_only;
850 0 : options.tcount = tcount;
851 :
852 0 : res = _SPI_execute_plan(&plan, &options,
853 : InvalidSnapshot, InvalidSnapshot,
854 : true);
855 :
856 0 : _SPI_end_call(true);
857 0 : return res;
858 : }
859 :
860 : SPIPlanPtr
861 3278 : SPI_prepare(const char *src, int nargs, Oid *argtypes)
862 : {
863 3278 : return SPI_prepare_cursor(src, nargs, argtypes, 0);
864 : }
865 :
866 : SPIPlanPtr
867 3278 : SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
868 : int cursorOptions)
869 : {
870 : _SPI_plan plan;
871 : SPIPlanPtr result;
872 :
873 3278 : if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
874 : {
875 0 : SPI_result = SPI_ERROR_ARGUMENT;
876 0 : return NULL;
877 : }
878 :
879 3278 : SPI_result = _SPI_begin_call(true);
880 3278 : if (SPI_result < 0)
881 0 : return NULL;
882 :
883 3278 : memset(&plan, 0, sizeof(_SPI_plan));
884 3278 : plan.magic = _SPI_PLAN_MAGIC;
885 3278 : plan.parse_mode = RAW_PARSE_DEFAULT;
886 3278 : plan.cursor_options = cursorOptions;
887 3278 : plan.nargs = nargs;
888 3278 : plan.argtypes = argtypes;
889 3278 : plan.parserSetup = NULL;
890 3278 : plan.parserSetupArg = NULL;
891 :
892 3278 : _SPI_prepare_plan(src, &plan);
893 :
894 : /* copy plan to procedure context */
895 3277 : result = _SPI_make_plan_non_temp(&plan);
896 :
897 3277 : _SPI_end_call(true);
898 :
899 3277 : return result;
900 : }
901 :
902 : SPIPlanPtr
903 17071 : SPI_prepare_extended(const char *src,
904 : const SPIPrepareOptions *options)
905 : {
906 : _SPI_plan plan;
907 : SPIPlanPtr result;
908 :
909 17071 : if (src == NULL || options == NULL)
910 : {
911 0 : SPI_result = SPI_ERROR_ARGUMENT;
912 0 : return NULL;
913 : }
914 :
915 17071 : SPI_result = _SPI_begin_call(true);
916 17071 : if (SPI_result < 0)
917 0 : return NULL;
918 :
919 17071 : memset(&plan, 0, sizeof(_SPI_plan));
920 17071 : plan.magic = _SPI_PLAN_MAGIC;
921 17071 : plan.parse_mode = options->parseMode;
922 17071 : plan.cursor_options = options->cursorOptions;
923 17071 : plan.nargs = 0;
924 17071 : plan.argtypes = NULL;
925 17071 : plan.parserSetup = options->parserSetup;
926 17071 : plan.parserSetupArg = options->parserSetupArg;
927 :
928 17071 : _SPI_prepare_plan(src, &plan);
929 :
930 : /* copy plan to procedure context */
931 17010 : result = _SPI_make_plan_non_temp(&plan);
932 :
933 17010 : _SPI_end_call(true);
934 :
935 17010 : return result;
936 : }
937 :
938 : SPIPlanPtr
939 0 : SPI_prepare_params(const char *src,
940 : ParserSetupHook parserSetup,
941 : void *parserSetupArg,
942 : int cursorOptions)
943 : {
944 : _SPI_plan plan;
945 : SPIPlanPtr result;
946 :
947 0 : if (src == NULL)
948 : {
949 0 : SPI_result = SPI_ERROR_ARGUMENT;
950 0 : return NULL;
951 : }
952 :
953 0 : SPI_result = _SPI_begin_call(true);
954 0 : if (SPI_result < 0)
955 0 : return NULL;
956 :
957 0 : memset(&plan, 0, sizeof(_SPI_plan));
958 0 : plan.magic = _SPI_PLAN_MAGIC;
959 0 : plan.parse_mode = RAW_PARSE_DEFAULT;
960 0 : plan.cursor_options = cursorOptions;
961 0 : plan.nargs = 0;
962 0 : plan.argtypes = NULL;
963 0 : plan.parserSetup = parserSetup;
964 0 : plan.parserSetupArg = parserSetupArg;
965 :
966 0 : _SPI_prepare_plan(src, &plan);
967 :
968 : /* copy plan to procedure context */
969 0 : result = _SPI_make_plan_non_temp(&plan);
970 :
971 0 : _SPI_end_call(true);
972 :
973 0 : return result;
974 : }
975 :
976 : int
977 19358 : SPI_keepplan(SPIPlanPtr plan)
978 : {
979 : ListCell *lc;
980 :
981 19358 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
982 19358 : plan->saved || plan->oneshot)
983 0 : return SPI_ERROR_ARGUMENT;
984 :
985 : /*
986 : * Mark it saved, reparent it under CacheMemoryContext, and mark all the
987 : * component CachedPlanSources as saved. This sequence cannot fail
988 : * partway through, so there's no risk of long-term memory leakage.
989 : */
990 19358 : plan->saved = true;
991 19358 : MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
992 :
993 38716 : foreach(lc, plan->plancache_list)
994 : {
995 19358 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
996 :
997 19358 : SaveCachedPlan(plansource);
998 : }
999 :
1000 19358 : return 0;
1001 : }
1002 :
1003 : SPIPlanPtr
1004 0 : SPI_saveplan(SPIPlanPtr plan)
1005 : {
1006 : SPIPlanPtr newplan;
1007 :
1008 0 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1009 : {
1010 0 : SPI_result = SPI_ERROR_ARGUMENT;
1011 0 : return NULL;
1012 : }
1013 :
1014 0 : SPI_result = _SPI_begin_call(false); /* don't change context */
1015 0 : if (SPI_result < 0)
1016 0 : return NULL;
1017 :
1018 0 : newplan = _SPI_save_plan(plan);
1019 :
1020 0 : SPI_result = _SPI_end_call(false);
1021 :
1022 0 : return newplan;
1023 : }
1024 :
1025 : int
1026 4468 : SPI_freeplan(SPIPlanPtr plan)
1027 : {
1028 : ListCell *lc;
1029 :
1030 4468 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1031 0 : return SPI_ERROR_ARGUMENT;
1032 :
1033 : /* Release the plancache entries */
1034 8936 : foreach(lc, plan->plancache_list)
1035 : {
1036 4468 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1037 :
1038 4468 : DropCachedPlan(plansource);
1039 : }
1040 :
1041 : /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
1042 4468 : MemoryContextDelete(plan->plancxt);
1043 :
1044 4468 : return 0;
1045 : }
1046 :
1047 : HeapTuple
1048 1364 : SPI_copytuple(HeapTuple tuple)
1049 : {
1050 : MemoryContext oldcxt;
1051 : HeapTuple ctuple;
1052 :
1053 1364 : if (tuple == NULL)
1054 : {
1055 0 : SPI_result = SPI_ERROR_ARGUMENT;
1056 0 : return NULL;
1057 : }
1058 :
1059 1364 : if (_SPI_current == NULL)
1060 : {
1061 0 : SPI_result = SPI_ERROR_UNCONNECTED;
1062 0 : return NULL;
1063 : }
1064 :
1065 1364 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1066 :
1067 1364 : ctuple = heap_copytuple(tuple);
1068 :
1069 1364 : MemoryContextSwitchTo(oldcxt);
1070 :
1071 1364 : return ctuple;
1072 : }
1073 :
1074 : HeapTupleHeader
1075 4108 : SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
1076 : {
1077 : MemoryContext oldcxt;
1078 : HeapTupleHeader dtup;
1079 :
1080 4108 : if (tuple == NULL || tupdesc == NULL)
1081 : {
1082 0 : SPI_result = SPI_ERROR_ARGUMENT;
1083 0 : return NULL;
1084 : }
1085 :
1086 4108 : if (_SPI_current == NULL)
1087 : {
1088 0 : SPI_result = SPI_ERROR_UNCONNECTED;
1089 0 : return NULL;
1090 : }
1091 :
1092 : /* For RECORD results, make sure a typmod has been assigned */
1093 4108 : if (tupdesc->tdtypeid == RECORDOID &&
1094 4099 : tupdesc->tdtypmod < 0)
1095 0 : assign_record_type_typmod(tupdesc);
1096 :
1097 4108 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1098 :
1099 4108 : dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
1100 :
1101 4108 : MemoryContextSwitchTo(oldcxt);
1102 :
1103 4108 : return dtup;
1104 : }
1105 :
1106 : HeapTuple
1107 0 : SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
1108 : Datum *Values, const char *Nulls)
1109 : {
1110 : MemoryContext oldcxt;
1111 : HeapTuple mtuple;
1112 : int numberOfAttributes;
1113 : Datum *v;
1114 : bool *n;
1115 : int i;
1116 :
1117 0 : if (rel == NULL || tuple == NULL || natts < 0 || attnum == NULL || Values == NULL)
1118 : {
1119 0 : SPI_result = SPI_ERROR_ARGUMENT;
1120 0 : return NULL;
1121 : }
1122 :
1123 0 : if (_SPI_current == NULL)
1124 : {
1125 0 : SPI_result = SPI_ERROR_UNCONNECTED;
1126 0 : return NULL;
1127 : }
1128 :
1129 0 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1130 :
1131 0 : SPI_result = 0;
1132 :
1133 0 : numberOfAttributes = rel->rd_att->natts;
1134 0 : v = palloc_array(Datum, numberOfAttributes);
1135 0 : n = palloc_array(bool, numberOfAttributes);
1136 :
1137 : /* fetch old values and nulls */
1138 0 : heap_deform_tuple(tuple, rel->rd_att, v, n);
1139 :
1140 : /* replace values and nulls */
1141 0 : for (i = 0; i < natts; i++)
1142 : {
1143 0 : if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
1144 : break;
1145 0 : v[attnum[i] - 1] = Values[i];
1146 0 : n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n');
1147 : }
1148 :
1149 0 : if (i == natts) /* no errors in *attnum */
1150 : {
1151 0 : mtuple = heap_form_tuple(rel->rd_att, v, n);
1152 :
1153 : /*
1154 : * copy the identification info of the old tuple: t_ctid, t_self, and
1155 : * OID (if any)
1156 : */
1157 0 : mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
1158 0 : mtuple->t_self = tuple->t_self;
1159 0 : mtuple->t_tableOid = tuple->t_tableOid;
1160 : }
1161 : else
1162 : {
1163 0 : mtuple = NULL;
1164 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1165 : }
1166 :
1167 0 : pfree(v);
1168 0 : pfree(n);
1169 :
1170 0 : MemoryContextSwitchTo(oldcxt);
1171 :
1172 0 : return mtuple;
1173 : }
1174 :
1175 : int
1176 12710 : SPI_fnumber(TupleDesc tupdesc, const char *fname)
1177 : {
1178 : int res;
1179 : const FormData_pg_attribute *sysatt;
1180 :
1181 68603 : for (res = 0; res < tupdesc->natts; res++)
1182 : {
1183 68596 : Form_pg_attribute attr = TupleDescAttr(tupdesc, res);
1184 :
1185 68596 : if (namestrcmp(&attr->attname, fname) == 0 &&
1186 12703 : !attr->attisdropped)
1187 12703 : return res + 1;
1188 : }
1189 :
1190 7 : sysatt = SystemAttributeByName(fname);
1191 7 : if (sysatt != NULL)
1192 0 : return sysatt->attnum;
1193 :
1194 : /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
1195 7 : return SPI_ERROR_NOATTRIBUTE;
1196 : }
1197 :
1198 : char *
1199 644 : SPI_fname(TupleDesc tupdesc, int fnumber)
1200 : {
1201 : const FormData_pg_attribute *att;
1202 :
1203 644 : SPI_result = 0;
1204 :
1205 644 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1206 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1207 : {
1208 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1209 0 : return NULL;
1210 : }
1211 :
1212 644 : if (fnumber > 0)
1213 644 : att = TupleDescAttr(tupdesc, fnumber - 1);
1214 : else
1215 0 : att = SystemAttributeDefinition(fnumber);
1216 :
1217 644 : return pstrdup(NameStr(att->attname));
1218 : }
1219 :
1220 : char *
1221 5449 : SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
1222 : {
1223 : Datum val;
1224 : bool isnull;
1225 : Oid typoid,
1226 : foutoid;
1227 : bool typisvarlena;
1228 :
1229 5449 : SPI_result = 0;
1230 :
1231 5449 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1232 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1233 : {
1234 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1235 0 : return NULL;
1236 : }
1237 :
1238 5449 : val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
1239 5449 : if (isnull)
1240 66 : return NULL;
1241 :
1242 5383 : if (fnumber > 0)
1243 5383 : typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1244 : else
1245 0 : typoid = (SystemAttributeDefinition(fnumber))->atttypid;
1246 :
1247 5383 : getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
1248 :
1249 5383 : return OidOutputFunctionCall(foutoid, val);
1250 : }
1251 :
1252 : Datum
1253 33566 : SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
1254 : {
1255 33566 : SPI_result = 0;
1256 :
1257 33566 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1258 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1259 : {
1260 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1261 0 : *isnull = true;
1262 0 : return (Datum) 0;
1263 : }
1264 :
1265 33566 : return heap_getattr(tuple, fnumber, tupdesc, isnull);
1266 : }
1267 :
1268 : char *
1269 4 : SPI_gettype(TupleDesc tupdesc, int fnumber)
1270 : {
1271 : Oid typoid;
1272 : HeapTuple typeTuple;
1273 : char *result;
1274 :
1275 4 : SPI_result = 0;
1276 :
1277 4 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1278 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1279 : {
1280 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1281 0 : return NULL;
1282 : }
1283 :
1284 4 : if (fnumber > 0)
1285 4 : typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1286 : else
1287 0 : typoid = (SystemAttributeDefinition(fnumber))->atttypid;
1288 :
1289 4 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
1290 :
1291 4 : if (!HeapTupleIsValid(typeTuple))
1292 : {
1293 0 : SPI_result = SPI_ERROR_TYPUNKNOWN;
1294 0 : return NULL;
1295 : }
1296 :
1297 4 : result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
1298 4 : ReleaseSysCache(typeTuple);
1299 4 : return result;
1300 : }
1301 :
1302 : /*
1303 : * Get the data type OID for a column.
1304 : *
1305 : * There's nothing similar for typmod and typcollation. The rare consumers
1306 : * thereof should inspect the TupleDesc directly.
1307 : */
1308 : Oid
1309 793 : SPI_gettypeid(TupleDesc tupdesc, int fnumber)
1310 : {
1311 793 : SPI_result = 0;
1312 :
1313 793 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1314 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1315 : {
1316 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1317 0 : return InvalidOid;
1318 : }
1319 :
1320 793 : if (fnumber > 0)
1321 793 : return TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1322 : else
1323 0 : return (SystemAttributeDefinition(fnumber))->atttypid;
1324 : }
1325 :
1326 : char *
1327 183 : SPI_getrelname(Relation rel)
1328 : {
1329 183 : return pstrdup(RelationGetRelationName(rel));
1330 : }
1331 :
1332 : char *
1333 141 : SPI_getnspname(Relation rel)
1334 : {
1335 141 : return get_namespace_name(RelationGetNamespace(rel));
1336 : }
1337 :
1338 : void *
1339 24 : SPI_palloc(Size size)
1340 : {
1341 24 : if (_SPI_current == NULL)
1342 0 : elog(ERROR, "SPI_palloc called while not connected to SPI");
1343 :
1344 24 : return MemoryContextAlloc(_SPI_current->savedcxt, size);
1345 : }
1346 :
1347 : void *
1348 0 : SPI_repalloc(void *pointer, Size size)
1349 : {
1350 : /* No longer need to worry which context chunk was in... */
1351 0 : return repalloc(pointer, size);
1352 : }
1353 :
1354 : void
1355 0 : SPI_pfree(void *pointer)
1356 : {
1357 : /* No longer need to worry which context chunk was in... */
1358 0 : pfree(pointer);
1359 0 : }
1360 :
1361 : Datum
1362 3726 : SPI_datumTransfer(Datum value, bool typByVal, int typLen)
1363 : {
1364 : MemoryContext oldcxt;
1365 : Datum result;
1366 :
1367 3726 : if (_SPI_current == NULL)
1368 0 : elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
1369 :
1370 3726 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1371 :
1372 3726 : result = datumTransfer(value, typByVal, typLen);
1373 :
1374 3726 : MemoryContextSwitchTo(oldcxt);
1375 :
1376 3726 : return result;
1377 : }
1378 :
1379 : void
1380 0 : SPI_freetuple(HeapTuple tuple)
1381 : {
1382 : /* No longer need to worry which context tuple was in... */
1383 0 : heap_freetuple(tuple);
1384 0 : }
1385 :
1386 : void
1387 521284 : SPI_freetuptable(SPITupleTable *tuptable)
1388 : {
1389 521284 : bool found = false;
1390 :
1391 : /* ignore call if NULL pointer */
1392 521284 : if (tuptable == NULL)
1393 468795 : return;
1394 :
1395 : /*
1396 : * Search only the topmost SPI context for a matching tuple table.
1397 : */
1398 52489 : if (_SPI_current != NULL)
1399 : {
1400 : slist_mutable_iter siter;
1401 :
1402 : /* find tuptable in active list, then remove it */
1403 52491 : slist_foreach_modify(siter, &_SPI_current->tuptables)
1404 : {
1405 : SPITupleTable *tt;
1406 :
1407 52491 : tt = slist_container(SPITupleTable, next, siter.cur);
1408 52491 : if (tt == tuptable)
1409 : {
1410 52489 : slist_delete_current(&siter);
1411 52489 : found = true;
1412 52489 : break;
1413 : }
1414 : }
1415 : }
1416 :
1417 : /*
1418 : * Refuse the deletion if we didn't find it in the topmost SPI context.
1419 : * This is primarily a guard against double deletion, but might prevent
1420 : * other errors as well. Since the worst consequence of not deleting a
1421 : * tuptable would be a transient memory leak, this is just a WARNING.
1422 : */
1423 52489 : if (!found)
1424 : {
1425 0 : elog(WARNING, "attempt to delete invalid SPITupleTable %p", tuptable);
1426 0 : return;
1427 : }
1428 :
1429 : /* for safety, reset global variables that might point at tuptable */
1430 52489 : if (tuptable == _SPI_current->tuptable)
1431 0 : _SPI_current->tuptable = NULL;
1432 52489 : if (tuptable == SPI_tuptable)
1433 47253 : SPI_tuptable = NULL;
1434 :
1435 : /* release all memory belonging to tuptable */
1436 52489 : MemoryContextDelete(tuptable->tuptabcxt);
1437 : }
1438 :
1439 :
1440 : /*
1441 : * SPI_cursor_open()
1442 : *
1443 : * Open a prepared SPI plan as a portal
1444 : */
1445 : Portal
1446 133 : SPI_cursor_open(const char *name, SPIPlanPtr plan,
1447 : const Datum *Values, const char *Nulls,
1448 : bool read_only)
1449 : {
1450 : Portal portal;
1451 : ParamListInfo paramLI;
1452 :
1453 : /* build transient ParamListInfo in caller's context */
1454 133 : paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
1455 : Values, Nulls);
1456 :
1457 133 : portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
1458 :
1459 : /* done with the transient ParamListInfo */
1460 133 : if (paramLI)
1461 4 : pfree(paramLI);
1462 :
1463 133 : return portal;
1464 : }
1465 :
1466 :
1467 : /*
1468 : * SPI_cursor_open_with_args()
1469 : *
1470 : * Parse and plan a query and open it as a portal.
1471 : */
1472 : Portal
1473 0 : SPI_cursor_open_with_args(const char *name,
1474 : const char *src,
1475 : int nargs, Oid *argtypes,
1476 : Datum *Values, const char *Nulls,
1477 : bool read_only, int cursorOptions)
1478 : {
1479 : Portal result;
1480 : _SPI_plan plan;
1481 : ParamListInfo paramLI;
1482 :
1483 0 : if (src == NULL || nargs < 0)
1484 0 : elog(ERROR, "SPI_cursor_open_with_args called with invalid arguments");
1485 :
1486 0 : if (nargs > 0 && (argtypes == NULL || Values == NULL))
1487 0 : elog(ERROR, "SPI_cursor_open_with_args called with missing parameters");
1488 :
1489 0 : SPI_result = _SPI_begin_call(true);
1490 0 : if (SPI_result < 0)
1491 0 : elog(ERROR, "SPI_cursor_open_with_args called while not connected");
1492 :
1493 0 : memset(&plan, 0, sizeof(_SPI_plan));
1494 0 : plan.magic = _SPI_PLAN_MAGIC;
1495 0 : plan.parse_mode = RAW_PARSE_DEFAULT;
1496 0 : plan.cursor_options = cursorOptions;
1497 0 : plan.nargs = nargs;
1498 0 : plan.argtypes = argtypes;
1499 0 : plan.parserSetup = NULL;
1500 0 : plan.parserSetupArg = NULL;
1501 :
1502 : /* build transient ParamListInfo in executor context */
1503 0 : paramLI = _SPI_convert_params(nargs, argtypes,
1504 : Values, Nulls);
1505 :
1506 0 : _SPI_prepare_plan(src, &plan);
1507 :
1508 : /* We needn't copy the plan; SPI_cursor_open_internal will do so */
1509 :
1510 0 : result = SPI_cursor_open_internal(name, &plan, paramLI, read_only);
1511 :
1512 : /* And clean up */
1513 0 : _SPI_end_call(true);
1514 :
1515 0 : return result;
1516 : }
1517 :
1518 :
1519 : /*
1520 : * SPI_cursor_open_with_paramlist()
1521 : *
1522 : * Same as SPI_cursor_open except that parameters (if any) are passed
1523 : * as a ParamListInfo, which supports dynamic parameter set determination
1524 : */
1525 : Portal
1526 1774 : SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
1527 : ParamListInfo params, bool read_only)
1528 : {
1529 1774 : return SPI_cursor_open_internal(name, plan, params, read_only);
1530 : }
1531 :
1532 : /* Parse a query and open it as a cursor */
1533 : Portal
1534 6367 : SPI_cursor_parse_open(const char *name,
1535 : const char *src,
1536 : const SPIParseOpenOptions *options)
1537 : {
1538 : Portal result;
1539 : _SPI_plan plan;
1540 :
1541 6367 : if (src == NULL || options == NULL)
1542 0 : elog(ERROR, "SPI_cursor_parse_open called with invalid arguments");
1543 :
1544 6367 : SPI_result = _SPI_begin_call(true);
1545 6367 : if (SPI_result < 0)
1546 0 : elog(ERROR, "SPI_cursor_parse_open called while not connected");
1547 :
1548 6367 : memset(&plan, 0, sizeof(_SPI_plan));
1549 6367 : plan.magic = _SPI_PLAN_MAGIC;
1550 6367 : plan.parse_mode = RAW_PARSE_DEFAULT;
1551 6367 : plan.cursor_options = options->cursorOptions;
1552 6367 : if (options->params)
1553 : {
1554 8 : plan.parserSetup = options->params->parserSetup;
1555 8 : plan.parserSetupArg = options->params->parserSetupArg;
1556 : }
1557 :
1558 6367 : _SPI_prepare_plan(src, &plan);
1559 :
1560 : /* We needn't copy the plan; SPI_cursor_open_internal will do so */
1561 :
1562 6367 : result = SPI_cursor_open_internal(name, &plan,
1563 6367 : options->params, options->read_only);
1564 :
1565 : /* And clean up */
1566 6367 : _SPI_end_call(true);
1567 :
1568 6367 : return result;
1569 : }
1570 :
1571 :
1572 : /*
1573 : * SPI_cursor_open_internal()
1574 : *
1575 : * Common code for SPI_cursor_open variants
1576 : */
1577 : static Portal
1578 8274 : SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
1579 : ParamListInfo paramLI, bool read_only)
1580 : {
1581 : CachedPlanSource *plansource;
1582 : CachedPlan *cplan;
1583 : List *stmt_list;
1584 : char *query_string;
1585 : Snapshot snapshot;
1586 : MemoryContext oldcontext;
1587 : Portal portal;
1588 : SPICallbackArg spicallbackarg;
1589 : ErrorContextCallback spierrcontext;
1590 :
1591 : /*
1592 : * Check that the plan is something the Portal code will special-case as
1593 : * returning one tupleset.
1594 : */
1595 8274 : if (!SPI_is_cursor_plan(plan))
1596 : {
1597 : /* try to give a good error message */
1598 : const char *cmdtag;
1599 :
1600 0 : if (list_length(plan->plancache_list) != 1)
1601 0 : ereport(ERROR,
1602 : (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
1603 : errmsg("cannot open multi-query plan as cursor")));
1604 0 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1605 : /* A SELECT that fails SPI_is_cursor_plan() must be SELECT INTO */
1606 0 : if (plansource->commandTag == CMDTAG_SELECT)
1607 0 : cmdtag = "SELECT INTO";
1608 : else
1609 0 : cmdtag = GetCommandTagName(plansource->commandTag);
1610 0 : ereport(ERROR,
1611 : (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
1612 : /* translator: %s is name of a SQL command, eg INSERT */
1613 : errmsg("cannot open %s query as cursor", cmdtag)));
1614 : }
1615 :
1616 : Assert(list_length(plan->plancache_list) == 1);
1617 8274 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1618 :
1619 : /* Push the SPI stack */
1620 8274 : if (_SPI_begin_call(true) < 0)
1621 0 : elog(ERROR, "SPI_cursor_open called while not connected");
1622 :
1623 : /* Reset SPI result (note we deliberately don't touch lastoid) */
1624 8274 : SPI_processed = 0;
1625 8274 : SPI_tuptable = NULL;
1626 8274 : _SPI_current->processed = 0;
1627 8274 : _SPI_current->tuptable = NULL;
1628 :
1629 : /* Create the portal */
1630 8274 : if (name == NULL || name[0] == '\0')
1631 : {
1632 : /* Use a random nonconflicting name */
1633 8242 : portal = CreateNewPortal();
1634 : }
1635 : else
1636 : {
1637 : /* In this path, error if portal of same name already exists */
1638 32 : portal = CreatePortal(name, false, false);
1639 : }
1640 :
1641 : /* Copy the plan's query string into the portal */
1642 8274 : query_string = MemoryContextStrdup(portal->portalContext,
1643 : plansource->query_string);
1644 :
1645 : /*
1646 : * Setup error traceback support for ereport(), in case GetCachedPlan
1647 : * throws an error.
1648 : */
1649 8274 : spicallbackarg.query = plansource->query_string;
1650 8274 : spicallbackarg.mode = plan->parse_mode;
1651 8274 : spierrcontext.callback = _SPI_error_callback;
1652 8274 : spierrcontext.arg = &spicallbackarg;
1653 8274 : spierrcontext.previous = error_context_stack;
1654 8274 : error_context_stack = &spierrcontext;
1655 :
1656 : /*
1657 : * Note: for a saved plan, we mustn't have any failure occur between
1658 : * GetCachedPlan and PortalDefineQuery; that would result in leaking our
1659 : * plancache refcount.
1660 : */
1661 :
1662 : /* Replan if needed, and increment plan refcount for portal */
1663 8274 : cplan = GetCachedPlan(plansource, paramLI, NULL, _SPI_current->queryEnv);
1664 8274 : stmt_list = cplan->stmt_list;
1665 :
1666 8274 : if (!plan->saved)
1667 : {
1668 : /*
1669 : * We don't want the portal to depend on an unsaved CachedPlanSource,
1670 : * so must copy the plan into the portal's context. An error here
1671 : * will result in leaking our refcount on the plan, but it doesn't
1672 : * matter because the plan is unsaved and hence transient anyway.
1673 : */
1674 6494 : oldcontext = MemoryContextSwitchTo(portal->portalContext);
1675 6494 : stmt_list = copyObject(stmt_list);
1676 6494 : MemoryContextSwitchTo(oldcontext);
1677 6494 : ReleaseCachedPlan(cplan, NULL);
1678 6494 : cplan = NULL; /* portal shouldn't depend on cplan */
1679 : }
1680 :
1681 : /*
1682 : * Set up the portal.
1683 : */
1684 8274 : PortalDefineQuery(portal,
1685 : NULL, /* no statement name */
1686 : query_string,
1687 : plansource->commandTag,
1688 : stmt_list,
1689 : cplan);
1690 :
1691 : /*
1692 : * Set up options for portal. Default SCROLL type is chosen the same way
1693 : * as PerformCursorOpen does it.
1694 : */
1695 8274 : portal->cursorOptions = plan->cursor_options;
1696 8274 : if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
1697 : {
1698 270 : if (list_length(stmt_list) == 1 &&
1699 270 : linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1700 539 : linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL &&
1701 269 : ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree))
1702 252 : portal->cursorOptions |= CURSOR_OPT_SCROLL;
1703 : else
1704 18 : portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
1705 : }
1706 :
1707 : /*
1708 : * Disallow SCROLL with SELECT FOR UPDATE. This is not redundant with the
1709 : * check in transformDeclareCursorStmt because the cursor options might
1710 : * not have come through there.
1711 : */
1712 8274 : if (portal->cursorOptions & CURSOR_OPT_SCROLL)
1713 : {
1714 269 : if (list_length(stmt_list) == 1 &&
1715 269 : linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1716 269 : linitial_node(PlannedStmt, stmt_list)->rowMarks != NIL)
1717 0 : ereport(ERROR,
1718 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1719 : errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"),
1720 : errdetail("Scrollable cursors must be READ ONLY.")));
1721 : }
1722 :
1723 : /* Make current query environment available to portal at execution time. */
1724 8274 : portal->queryEnv = _SPI_current->queryEnv;
1725 :
1726 : /*
1727 : * If told to be read-only, we'd better check for read-only queries. This
1728 : * can't be done earlier because we need to look at the finished, planned
1729 : * queries. (In particular, we don't want to do it between GetCachedPlan
1730 : * and PortalDefineQuery, because throwing an error between those steps
1731 : * would result in leaking our plancache refcount.)
1732 : */
1733 8274 : if (read_only)
1734 : {
1735 : ListCell *lc;
1736 :
1737 208 : foreach(lc, stmt_list)
1738 : {
1739 104 : PlannedStmt *pstmt = lfirst_node(PlannedStmt, lc);
1740 :
1741 104 : if (!CommandIsReadOnly(pstmt))
1742 0 : ereport(ERROR,
1743 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1744 : /* translator: %s is a SQL statement name */
1745 : errmsg("%s is not allowed in a non-volatile function",
1746 : CreateCommandName((Node *) pstmt))));
1747 : }
1748 : }
1749 :
1750 : /* Set up the snapshot to use. */
1751 8274 : if (read_only)
1752 104 : snapshot = GetActiveSnapshot();
1753 : else
1754 : {
1755 8170 : CommandCounterIncrement();
1756 8170 : snapshot = GetTransactionSnapshot();
1757 : }
1758 :
1759 : /*
1760 : * If the plan has parameters, copy them into the portal. Note that this
1761 : * must be done after revalidating the plan, because in dynamic parameter
1762 : * cases the set of parameters could have changed during re-parsing.
1763 : */
1764 8274 : if (paramLI)
1765 : {
1766 444 : oldcontext = MemoryContextSwitchTo(portal->portalContext);
1767 444 : paramLI = copyParamList(paramLI);
1768 444 : MemoryContextSwitchTo(oldcontext);
1769 : }
1770 :
1771 : /*
1772 : * Start portal execution.
1773 : */
1774 8274 : PortalStart(portal, paramLI, 0, snapshot);
1775 :
1776 : Assert(portal->strategy != PORTAL_MULTI_QUERY);
1777 :
1778 : /* Pop the error context stack */
1779 8274 : error_context_stack = spierrcontext.previous;
1780 :
1781 : /* Pop the SPI stack */
1782 8274 : _SPI_end_call(true);
1783 :
1784 : /* Return the created portal */
1785 8274 : return portal;
1786 : }
1787 :
1788 :
1789 : /*
1790 : * SPI_cursor_find()
1791 : *
1792 : * Find the portal of an existing open cursor
1793 : */
1794 : Portal
1795 362 : SPI_cursor_find(const char *name)
1796 : {
1797 362 : return GetPortalByName(name);
1798 : }
1799 :
1800 :
1801 : /*
1802 : * SPI_cursor_fetch()
1803 : *
1804 : * Fetch rows in a cursor
1805 : */
1806 : void
1807 29567 : SPI_cursor_fetch(Portal portal, bool forward, long count)
1808 : {
1809 29567 : _SPI_cursor_operation(portal,
1810 29567 : forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1811 : CreateDestReceiver(DestSPI));
1812 : /* we know that the DestSPI receiver doesn't need a destroy call */
1813 29562 : }
1814 :
1815 :
1816 : /*
1817 : * SPI_cursor_move()
1818 : *
1819 : * Move in a cursor
1820 : */
1821 : void
1822 0 : SPI_cursor_move(Portal portal, bool forward, long count)
1823 : {
1824 0 : _SPI_cursor_operation(portal,
1825 0 : forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1826 : None_Receiver);
1827 0 : }
1828 :
1829 :
1830 : /*
1831 : * SPI_scroll_cursor_fetch()
1832 : *
1833 : * Fetch rows in a scrollable cursor
1834 : */
1835 : void
1836 201 : SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
1837 : {
1838 201 : _SPI_cursor_operation(portal,
1839 : direction, count,
1840 : CreateDestReceiver(DestSPI));
1841 : /* we know that the DestSPI receiver doesn't need a destroy call */
1842 197 : }
1843 :
1844 :
1845 : /*
1846 : * SPI_scroll_cursor_move()
1847 : *
1848 : * Move in a scrollable cursor
1849 : */
1850 : void
1851 28 : SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
1852 : {
1853 28 : _SPI_cursor_operation(portal, direction, count, None_Receiver);
1854 28 : }
1855 :
1856 :
1857 : /*
1858 : * SPI_cursor_close()
1859 : *
1860 : * Close a cursor
1861 : */
1862 : void
1863 8205 : SPI_cursor_close(Portal portal)
1864 : {
1865 8205 : if (!PortalIsValid(portal))
1866 0 : elog(ERROR, "invalid portal in SPI cursor operation");
1867 :
1868 8205 : PortalDrop(portal, false);
1869 8205 : }
1870 :
1871 : /*
1872 : * Returns the Oid representing the type id for argument at argIndex. First
1873 : * parameter is at index zero.
1874 : */
1875 : Oid
1876 0 : SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
1877 : {
1878 0 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
1879 0 : argIndex < 0 || argIndex >= plan->nargs)
1880 : {
1881 0 : SPI_result = SPI_ERROR_ARGUMENT;
1882 0 : return InvalidOid;
1883 : }
1884 0 : return plan->argtypes[argIndex];
1885 : }
1886 :
1887 : /*
1888 : * Returns the number of arguments for the prepared plan.
1889 : */
1890 : int
1891 0 : SPI_getargcount(SPIPlanPtr plan)
1892 : {
1893 0 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1894 : {
1895 0 : SPI_result = SPI_ERROR_ARGUMENT;
1896 0 : return -1;
1897 : }
1898 0 : return plan->nargs;
1899 : }
1900 :
1901 : /*
1902 : * Returns true if the plan contains exactly one command
1903 : * and that command returns tuples to the caller (eg, SELECT or
1904 : * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
1905 : * the result indicates if the command can be used with SPI_cursor_open
1906 : *
1907 : * Parameters
1908 : * plan: A plan previously prepared using SPI_prepare
1909 : */
1910 : bool
1911 8274 : SPI_is_cursor_plan(SPIPlanPtr plan)
1912 : {
1913 : CachedPlanSource *plansource;
1914 :
1915 8274 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1916 : {
1917 0 : SPI_result = SPI_ERROR_ARGUMENT;
1918 0 : return false;
1919 : }
1920 :
1921 8274 : if (list_length(plan->plancache_list) != 1)
1922 : {
1923 0 : SPI_result = 0;
1924 0 : return false; /* not exactly 1 pre-rewrite command */
1925 : }
1926 8274 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1927 :
1928 : /*
1929 : * We used to force revalidation of the cached plan here, but that seems
1930 : * unnecessary: invalidation could mean a change in the rowtype of the
1931 : * tuples returned by a plan, but not whether it returns tuples at all.
1932 : */
1933 8274 : SPI_result = 0;
1934 :
1935 : /* Does it return tuples? */
1936 8274 : if (plansource->resultDesc)
1937 8274 : return true;
1938 :
1939 0 : return false;
1940 : }
1941 :
1942 : /*
1943 : * SPI_plan_is_valid --- test whether a SPI plan is currently valid
1944 : * (that is, not marked as being in need of revalidation).
1945 : *
1946 : * See notes for CachedPlanIsValid before using this.
1947 : */
1948 : bool
1949 402535 : SPI_plan_is_valid(SPIPlanPtr plan)
1950 : {
1951 : ListCell *lc;
1952 :
1953 : Assert(plan->magic == _SPI_PLAN_MAGIC);
1954 :
1955 804814 : foreach(lc, plan->plancache_list)
1956 : {
1957 402535 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1958 :
1959 402535 : if (!CachedPlanIsValid(plansource))
1960 256 : return false;
1961 : }
1962 402279 : return true;
1963 : }
1964 :
1965 : /*
1966 : * SPI_result_code_string --- convert any SPI return code to a string
1967 : *
1968 : * This is often useful in error messages. Most callers will probably
1969 : * only pass negative (error-case) codes, but for generality we recognize
1970 : * the success codes too.
1971 : */
1972 : const char *
1973 60 : SPI_result_code_string(int code)
1974 : {
1975 : static char buf[64];
1976 :
1977 60 : switch (code)
1978 : {
1979 0 : case SPI_ERROR_CONNECT:
1980 0 : return "SPI_ERROR_CONNECT";
1981 0 : case SPI_ERROR_COPY:
1982 0 : return "SPI_ERROR_COPY";
1983 0 : case SPI_ERROR_OPUNKNOWN:
1984 0 : return "SPI_ERROR_OPUNKNOWN";
1985 0 : case SPI_ERROR_UNCONNECTED:
1986 0 : return "SPI_ERROR_UNCONNECTED";
1987 0 : case SPI_ERROR_ARGUMENT:
1988 0 : return "SPI_ERROR_ARGUMENT";
1989 0 : case SPI_ERROR_PARAM:
1990 0 : return "SPI_ERROR_PARAM";
1991 3 : case SPI_ERROR_TRANSACTION:
1992 3 : return "SPI_ERROR_TRANSACTION";
1993 0 : case SPI_ERROR_NOATTRIBUTE:
1994 0 : return "SPI_ERROR_NOATTRIBUTE";
1995 0 : case SPI_ERROR_NOOUTFUNC:
1996 0 : return "SPI_ERROR_NOOUTFUNC";
1997 0 : case SPI_ERROR_TYPUNKNOWN:
1998 0 : return "SPI_ERROR_TYPUNKNOWN";
1999 0 : case SPI_ERROR_REL_DUPLICATE:
2000 0 : return "SPI_ERROR_REL_DUPLICATE";
2001 0 : case SPI_ERROR_REL_NOT_FOUND:
2002 0 : return "SPI_ERROR_REL_NOT_FOUND";
2003 0 : case SPI_OK_CONNECT:
2004 0 : return "SPI_OK_CONNECT";
2005 0 : case SPI_OK_FINISH:
2006 0 : return "SPI_OK_FINISH";
2007 0 : case SPI_OK_FETCH:
2008 0 : return "SPI_OK_FETCH";
2009 1 : case SPI_OK_UTILITY:
2010 1 : return "SPI_OK_UTILITY";
2011 11 : case SPI_OK_SELECT:
2012 11 : return "SPI_OK_SELECT";
2013 0 : case SPI_OK_SELINTO:
2014 0 : return "SPI_OK_SELINTO";
2015 45 : case SPI_OK_INSERT:
2016 45 : return "SPI_OK_INSERT";
2017 0 : case SPI_OK_DELETE:
2018 0 : return "SPI_OK_DELETE";
2019 0 : case SPI_OK_UPDATE:
2020 0 : return "SPI_OK_UPDATE";
2021 0 : case SPI_OK_CURSOR:
2022 0 : return "SPI_OK_CURSOR";
2023 0 : case SPI_OK_INSERT_RETURNING:
2024 0 : return "SPI_OK_INSERT_RETURNING";
2025 0 : case SPI_OK_DELETE_RETURNING:
2026 0 : return "SPI_OK_DELETE_RETURNING";
2027 0 : case SPI_OK_UPDATE_RETURNING:
2028 0 : return "SPI_OK_UPDATE_RETURNING";
2029 0 : case SPI_OK_REWRITTEN:
2030 0 : return "SPI_OK_REWRITTEN";
2031 0 : case SPI_OK_REL_REGISTER:
2032 0 : return "SPI_OK_REL_REGISTER";
2033 0 : case SPI_OK_REL_UNREGISTER:
2034 0 : return "SPI_OK_REL_UNREGISTER";
2035 0 : case SPI_OK_TD_REGISTER:
2036 0 : return "SPI_OK_TD_REGISTER";
2037 0 : case SPI_OK_MERGE:
2038 0 : return "SPI_OK_MERGE";
2039 0 : case SPI_OK_MERGE_RETURNING:
2040 0 : return "SPI_OK_MERGE_RETURNING";
2041 : }
2042 : /* Unrecognized code ... return something useful ... */
2043 0 : sprintf(buf, "Unrecognized SPI code %d", code);
2044 0 : return buf;
2045 : }
2046 :
2047 : /*
2048 : * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
2049 : * CachedPlanSources.
2050 : *
2051 : * CAUTION: there is no check on whether the CachedPlanSources are up-to-date.
2052 : *
2053 : * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
2054 : * look directly into the SPIPlan for itself). It's not documented in
2055 : * spi.sgml because we'd just as soon not have too many places using this.
2056 : */
2057 : List *
2058 36623 : SPI_plan_get_plan_sources(SPIPlanPtr plan)
2059 : {
2060 : Assert(plan->magic == _SPI_PLAN_MAGIC);
2061 36623 : return plan->plancache_list;
2062 : }
2063 :
2064 : /*
2065 : * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
2066 : * if the SPI plan contains exactly one CachedPlanSource. If not,
2067 : * return NULL.
2068 : *
2069 : * The plan's refcount is incremented (and logged in CurrentResourceOwner,
2070 : * if it's a saved plan). Caller is responsible for doing ReleaseCachedPlan.
2071 : *
2072 : * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
2073 : * look directly into the SPIPlan for itself). It's not documented in
2074 : * spi.sgml because we'd just as soon not have too many places using this.
2075 : */
2076 : CachedPlan *
2077 18079 : SPI_plan_get_cached_plan(SPIPlanPtr plan)
2078 : {
2079 : CachedPlanSource *plansource;
2080 : CachedPlan *cplan;
2081 : SPICallbackArg spicallbackarg;
2082 : ErrorContextCallback spierrcontext;
2083 :
2084 : Assert(plan->magic == _SPI_PLAN_MAGIC);
2085 :
2086 : /* Can't support one-shot plans here */
2087 18079 : if (plan->oneshot)
2088 0 : return NULL;
2089 :
2090 : /* Must have exactly one CachedPlanSource */
2091 18079 : if (list_length(plan->plancache_list) != 1)
2092 0 : return NULL;
2093 18079 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
2094 :
2095 : /* Setup error traceback support for ereport() */
2096 18079 : spicallbackarg.query = plansource->query_string;
2097 18079 : spicallbackarg.mode = plan->parse_mode;
2098 18079 : spierrcontext.callback = _SPI_error_callback;
2099 18079 : spierrcontext.arg = &spicallbackarg;
2100 18079 : spierrcontext.previous = error_context_stack;
2101 18079 : error_context_stack = &spierrcontext;
2102 :
2103 : /* Get the generic plan for the query */
2104 18079 : cplan = GetCachedPlan(plansource, NULL,
2105 18079 : plan->saved ? CurrentResourceOwner : NULL,
2106 18079 : _SPI_current->queryEnv);
2107 : Assert(cplan == plansource->gplan);
2108 :
2109 : /* Pop the error context stack */
2110 18054 : error_context_stack = spierrcontext.previous;
2111 :
2112 18054 : return cplan;
2113 : }
2114 :
2115 :
2116 : /* =================== private functions =================== */
2117 :
2118 : /*
2119 : * spi_dest_startup
2120 : * Initialize to receive tuples from Executor into SPITupleTable
2121 : * of current SPI procedure
2122 : */
2123 : void
2124 463689 : spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
2125 : {
2126 : SPITupleTable *tuptable;
2127 : MemoryContext oldcxt;
2128 : MemoryContext tuptabcxt;
2129 :
2130 463689 : if (_SPI_current == NULL)
2131 0 : elog(ERROR, "spi_dest_startup called while not connected to SPI");
2132 :
2133 463689 : if (_SPI_current->tuptable != NULL)
2134 0 : elog(ERROR, "improper call to spi_dest_startup");
2135 :
2136 : /* We create the tuple table context as a child of procCxt */
2137 :
2138 463689 : oldcxt = _SPI_procmem(); /* switch to procedure memory context */
2139 :
2140 463689 : tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
2141 : "SPI TupTable",
2142 : ALLOCSET_DEFAULT_SIZES);
2143 463689 : MemoryContextSwitchTo(tuptabcxt);
2144 :
2145 463689 : _SPI_current->tuptable = tuptable = palloc0_object(SPITupleTable);
2146 463689 : tuptable->tuptabcxt = tuptabcxt;
2147 463689 : tuptable->subid = GetCurrentSubTransactionId();
2148 :
2149 : /*
2150 : * The tuptable is now valid enough to be freed by AtEOSubXact_SPI, so put
2151 : * it onto the SPI context's tuptables list. This will ensure it's not
2152 : * leaked even in the unlikely event the following few lines fail.
2153 : */
2154 463689 : slist_push_head(&_SPI_current->tuptables, &tuptable->next);
2155 :
2156 : /* set up initial allocations */
2157 463689 : tuptable->alloced = 128;
2158 463689 : tuptable->vals = palloc_array(HeapTuple, tuptable->alloced);
2159 463689 : tuptable->numvals = 0;
2160 463689 : tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
2161 :
2162 463689 : MemoryContextSwitchTo(oldcxt);
2163 463689 : }
2164 :
2165 : /*
2166 : * spi_printtup
2167 : * store tuple retrieved by Executor into SPITupleTable
2168 : * of current SPI procedure
2169 : */
2170 : bool
2171 475861 : spi_printtup(TupleTableSlot *slot, DestReceiver *self)
2172 : {
2173 : SPITupleTable *tuptable;
2174 : MemoryContext oldcxt;
2175 :
2176 475861 : if (_SPI_current == NULL)
2177 0 : elog(ERROR, "spi_printtup called while not connected to SPI");
2178 :
2179 475861 : tuptable = _SPI_current->tuptable;
2180 475861 : if (tuptable == NULL)
2181 0 : elog(ERROR, "improper call to spi_printtup");
2182 :
2183 475861 : oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
2184 :
2185 475861 : if (tuptable->numvals >= tuptable->alloced)
2186 : {
2187 : /* Double the size of the pointer array */
2188 0 : uint64 newalloced = tuptable->alloced * 2;
2189 :
2190 0 : tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
2191 : newalloced * sizeof(HeapTuple));
2192 0 : tuptable->alloced = newalloced;
2193 : }
2194 :
2195 475861 : tuptable->vals[tuptable->numvals] = ExecCopySlotHeapTuple(slot);
2196 475861 : (tuptable->numvals)++;
2197 :
2198 475861 : MemoryContextSwitchTo(oldcxt);
2199 :
2200 475861 : return true;
2201 : }
2202 :
2203 : /*
2204 : * Static functions
2205 : */
2206 :
2207 : /*
2208 : * Parse and analyze a querystring.
2209 : *
2210 : * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
2211 : * and plan->parserSetupArg) must be valid, as must plan->parse_mode and
2212 : * plan->cursor_options.
2213 : *
2214 : * Results are stored into *plan (specifically, plan->plancache_list).
2215 : * Note that the result data is all in CurrentMemoryContext or child contexts
2216 : * thereof; in practice this means it is in the SPI executor context, and
2217 : * what we are creating is a "temporary" SPIPlan. Cruft generated during
2218 : * parsing is also left in CurrentMemoryContext.
2219 : */
2220 : static void
2221 26716 : _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
2222 : {
2223 : List *raw_parsetree_list;
2224 : List *plancache_list;
2225 : ListCell *list_item;
2226 : SPICallbackArg spicallbackarg;
2227 : ErrorContextCallback spierrcontext;
2228 :
2229 : /*
2230 : * Setup error traceback support for ereport()
2231 : */
2232 26716 : spicallbackarg.query = src;
2233 26716 : spicallbackarg.mode = plan->parse_mode;
2234 26716 : spierrcontext.callback = _SPI_error_callback;
2235 26716 : spierrcontext.arg = &spicallbackarg;
2236 26716 : spierrcontext.previous = error_context_stack;
2237 26716 : error_context_stack = &spierrcontext;
2238 :
2239 : /*
2240 : * Parse the request string into a list of raw parse trees.
2241 : */
2242 26716 : raw_parsetree_list = raw_parser(src, plan->parse_mode);
2243 :
2244 : /*
2245 : * Do parse analysis and rule rewrite for each raw parsetree, storing the
2246 : * results into unsaved plancache entries.
2247 : */
2248 26716 : plancache_list = NIL;
2249 :
2250 53370 : foreach(list_item, raw_parsetree_list)
2251 : {
2252 26716 : RawStmt *parsetree = lfirst_node(RawStmt, list_item);
2253 : List *stmt_list;
2254 : CachedPlanSource *plansource;
2255 :
2256 : /*
2257 : * Create the CachedPlanSource before we do parse analysis, since it
2258 : * needs to see the unmodified raw parse tree.
2259 : */
2260 26716 : plansource = CreateCachedPlan(parsetree,
2261 : src,
2262 : CreateCommandTag(parsetree->stmt));
2263 :
2264 : /*
2265 : * Parameter datatypes are driven by parserSetup hook if provided,
2266 : * otherwise we use the fixed parameter list.
2267 : */
2268 26716 : if (plan->parserSetup != NULL)
2269 : {
2270 : Assert(plan->nargs == 0);
2271 17079 : stmt_list = pg_analyze_and_rewrite_withcb(parsetree,
2272 : src,
2273 : plan->parserSetup,
2274 : plan->parserSetupArg,
2275 17079 : _SPI_current->queryEnv);
2276 : }
2277 : else
2278 : {
2279 9637 : stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
2280 : src,
2281 9637 : plan->argtypes,
2282 : plan->nargs,
2283 9637 : _SPI_current->queryEnv);
2284 : }
2285 :
2286 : /* Finish filling in the CachedPlanSource */
2287 26654 : CompleteCachedPlan(plansource,
2288 : stmt_list,
2289 : NULL,
2290 : plan->argtypes,
2291 : plan->nargs,
2292 : plan->parserSetup,
2293 : plan->parserSetupArg,
2294 : plan->cursor_options,
2295 : false); /* not fixed result */
2296 :
2297 26654 : plancache_list = lappend(plancache_list, plansource);
2298 : }
2299 :
2300 26654 : plan->plancache_list = plancache_list;
2301 26654 : plan->oneshot = false;
2302 :
2303 : /*
2304 : * Pop the error context stack
2305 : */
2306 26654 : error_context_stack = spierrcontext.previous;
2307 26654 : }
2308 :
2309 : /*
2310 : * Parse, but don't analyze, a querystring.
2311 : *
2312 : * This is a stripped-down version of _SPI_prepare_plan that only does the
2313 : * initial raw parsing. It creates "one shot" CachedPlanSources
2314 : * that still require parse analysis before execution is possible.
2315 : *
2316 : * The advantage of using the "one shot" form of CachedPlanSource is that
2317 : * we eliminate data copying and invalidation overhead. Postponing parse
2318 : * analysis also prevents issues if some of the raw parsetrees are DDL
2319 : * commands that affect validity of later parsetrees. Both of these
2320 : * attributes are good things for SPI_execute() and similar cases.
2321 : *
2322 : * Results are stored into *plan (specifically, plan->plancache_list).
2323 : * Note that the result data is all in CurrentMemoryContext or child contexts
2324 : * thereof; in practice this means it is in the SPI executor context, and
2325 : * what we are creating is a "temporary" SPIPlan. Cruft generated during
2326 : * parsing is also left in CurrentMemoryContext.
2327 : */
2328 : static void
2329 11625 : _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
2330 : {
2331 : List *raw_parsetree_list;
2332 : List *plancache_list;
2333 : ListCell *list_item;
2334 : SPICallbackArg spicallbackarg;
2335 : ErrorContextCallback spierrcontext;
2336 :
2337 : /*
2338 : * Setup error traceback support for ereport()
2339 : */
2340 11625 : spicallbackarg.query = src;
2341 11625 : spicallbackarg.mode = plan->parse_mode;
2342 11625 : spierrcontext.callback = _SPI_error_callback;
2343 11625 : spierrcontext.arg = &spicallbackarg;
2344 11625 : spierrcontext.previous = error_context_stack;
2345 11625 : error_context_stack = &spierrcontext;
2346 :
2347 : /*
2348 : * Parse the request string into a list of raw parse trees.
2349 : */
2350 11625 : raw_parsetree_list = raw_parser(src, plan->parse_mode);
2351 :
2352 : /*
2353 : * Construct plancache entries, but don't do parse analysis yet.
2354 : */
2355 11617 : plancache_list = NIL;
2356 :
2357 23239 : foreach(list_item, raw_parsetree_list)
2358 : {
2359 11622 : RawStmt *parsetree = lfirst_node(RawStmt, list_item);
2360 : CachedPlanSource *plansource;
2361 :
2362 11622 : plansource = CreateOneShotCachedPlan(parsetree,
2363 : src,
2364 : CreateCommandTag(parsetree->stmt));
2365 :
2366 11622 : plancache_list = lappend(plancache_list, plansource);
2367 : }
2368 :
2369 11617 : plan->plancache_list = plancache_list;
2370 11617 : plan->oneshot = true;
2371 :
2372 : /*
2373 : * Pop the error context stack
2374 : */
2375 11617 : error_context_stack = spierrcontext.previous;
2376 11617 : }
2377 :
2378 : /*
2379 : * _SPI_execute_plan: execute the given plan with the given options
2380 : *
2381 : * options contains options accessible from outside SPI:
2382 : * params: parameter values to pass to query
2383 : * read_only: true for read-only execution (no CommandCounterIncrement)
2384 : * allow_nonatomic: true to allow nonatomic CALL/DO execution
2385 : * must_return_tuples: throw error if query doesn't return tuples
2386 : * tcount: execution tuple-count limit, or 0 for none
2387 : * dest: DestReceiver to receive output, or NULL for normal SPI output
2388 : * owner: ResourceOwner that will be used to hold refcount on plan;
2389 : * if NULL, CurrentResourceOwner is used (ignored for non-saved plan)
2390 : *
2391 : * Additional, only-internally-accessible options:
2392 : * snapshot: query snapshot to use, or InvalidSnapshot for the normal
2393 : * behavior of taking a new snapshot for each query.
2394 : * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
2395 : * fire_triggers: true to fire AFTER triggers at end of query (normal case);
2396 : * false means any AFTER triggers are postponed to end of outer query
2397 : */
2398 : static int
2399 469206 : _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
2400 : Snapshot snapshot, Snapshot crosscheck_snapshot,
2401 : bool fire_triggers)
2402 : {
2403 469206 : int my_res = 0;
2404 469206 : uint64 my_processed = 0;
2405 469206 : SPITupleTable *my_tuptable = NULL;
2406 469206 : int res = 0;
2407 : bool allow_nonatomic;
2408 469206 : bool pushed_active_snap = false;
2409 469206 : ResourceOwner plan_owner = options->owner;
2410 : SPICallbackArg spicallbackarg;
2411 : ErrorContextCallback spierrcontext;
2412 469206 : CachedPlan *cplan = NULL;
2413 : ListCell *lc1;
2414 :
2415 : /*
2416 : * We allow nonatomic behavior only if options->allow_nonatomic is set
2417 : * *and* the SPI_OPT_NONATOMIC flag was given when connecting and we are
2418 : * not inside a subtransaction. The latter two tests match whether
2419 : * _SPI_commit() would allow a commit; see there for more commentary.
2420 : */
2421 938471 : allow_nonatomic = options->allow_nonatomic &&
2422 469206 : !_SPI_current->atomic && !IsSubTransaction();
2423 :
2424 : /*
2425 : * Setup error traceback support for ereport()
2426 : */
2427 469206 : spicallbackarg.query = NULL; /* we'll fill this below */
2428 469206 : spicallbackarg.mode = plan->parse_mode;
2429 469206 : spierrcontext.callback = _SPI_error_callback;
2430 469206 : spierrcontext.arg = &spicallbackarg;
2431 469206 : spierrcontext.previous = error_context_stack;
2432 469206 : error_context_stack = &spierrcontext;
2433 :
2434 : /*
2435 : * We support four distinct snapshot management behaviors:
2436 : *
2437 : * snapshot != InvalidSnapshot, read_only = true: use exactly the given
2438 : * snapshot.
2439 : *
2440 : * snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
2441 : * modified by advancing its command ID before each querytree.
2442 : *
2443 : * snapshot == InvalidSnapshot, read_only = true: do nothing for queries
2444 : * that require no snapshot. For those that do, ensure that a Portal
2445 : * snapshot exists; then use that, or use the entry-time ActiveSnapshot if
2446 : * that exists and is different.
2447 : *
2448 : * snapshot == InvalidSnapshot, read_only = false: do nothing for queries
2449 : * that require no snapshot. For those that do, ensure that a Portal
2450 : * snapshot exists; then, in atomic execution (!allow_nonatomic) take a
2451 : * full new snapshot for each user command, and advance its command ID
2452 : * before each querytree within the command. In allow_nonatomic mode we
2453 : * just use the Portal snapshot unmodified.
2454 : *
2455 : * In the first two cases, we can just push the snap onto the stack once
2456 : * for the whole plan list.
2457 : *
2458 : * Note that snapshot != InvalidSnapshot implies an atomic execution
2459 : * context.
2460 : */
2461 469206 : if (snapshot != InvalidSnapshot)
2462 : {
2463 : /* this intentionally tests the options field not the derived value */
2464 : Assert(!options->allow_nonatomic);
2465 764 : if (options->read_only)
2466 : {
2467 728 : PushActiveSnapshot(snapshot);
2468 728 : pushed_active_snap = true;
2469 : }
2470 : else
2471 : {
2472 : /* Make sure we have a private copy of the snapshot to modify */
2473 36 : PushCopiedSnapshot(snapshot);
2474 36 : pushed_active_snap = true;
2475 : }
2476 : }
2477 :
2478 : /*
2479 : * Ensure that we have a resource owner if plan is saved, and not if it
2480 : * isn't.
2481 : */
2482 469206 : if (!plan->saved)
2483 12419 : plan_owner = NULL;
2484 456787 : else if (plan_owner == NULL)
2485 456732 : plan_owner = CurrentResourceOwner;
2486 :
2487 : /*
2488 : * We interpret must_return_tuples as "there must be at least one query,
2489 : * and all of them must return tuples". This is a bit laxer than
2490 : * SPI_is_cursor_plan's check, but there seems no reason to enforce that
2491 : * there be only one query.
2492 : */
2493 469206 : if (options->must_return_tuples && plan->plancache_list == NIL)
2494 0 : ereport(ERROR,
2495 : (errcode(ERRCODE_SYNTAX_ERROR),
2496 : errmsg("empty query does not return tuples")));
2497 :
2498 934573 : foreach(lc1, plan->plancache_list)
2499 : {
2500 469210 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
2501 : List *stmt_list;
2502 : ListCell *lc2;
2503 :
2504 469210 : spicallbackarg.query = plansource->query_string;
2505 :
2506 : /*
2507 : * If this is a one-shot plan, we still need to do parse analysis.
2508 : */
2509 469210 : if (plan->oneshot)
2510 : {
2511 11621 : RawStmt *parsetree = plansource->raw_parse_tree;
2512 11621 : const char *src = plansource->query_string;
2513 : List *querytree_list;
2514 :
2515 : /*
2516 : * Parameter datatypes are driven by parserSetup hook if provided,
2517 : * otherwise we use the fixed parameter list.
2518 : */
2519 11621 : if (parsetree == NULL)
2520 0 : querytree_list = NIL;
2521 11621 : else if (plan->parserSetup != NULL)
2522 : {
2523 : Assert(plan->nargs == 0);
2524 289 : querytree_list = pg_analyze_and_rewrite_withcb(parsetree,
2525 : src,
2526 : plan->parserSetup,
2527 : plan->parserSetupArg,
2528 289 : _SPI_current->queryEnv);
2529 : }
2530 : else
2531 : {
2532 11332 : querytree_list = pg_analyze_and_rewrite_fixedparams(parsetree,
2533 : src,
2534 11332 : plan->argtypes,
2535 : plan->nargs,
2536 11332 : _SPI_current->queryEnv);
2537 : }
2538 :
2539 : /* Finish filling in the CachedPlanSource */
2540 11614 : CompleteCachedPlan(plansource,
2541 : querytree_list,
2542 : NULL,
2543 : plan->argtypes,
2544 : plan->nargs,
2545 : plan->parserSetup,
2546 : plan->parserSetupArg,
2547 : plan->cursor_options,
2548 : false); /* not fixed result */
2549 : }
2550 :
2551 : /*
2552 : * If asked to, complain when query does not return tuples.
2553 : * (Replanning can't change this, so we can check it before that.
2554 : * However, we can't check it till after parse analysis, so in the
2555 : * case of a one-shot plan this is the earliest we could check.)
2556 : */
2557 469203 : if (options->must_return_tuples && !plansource->resultDesc)
2558 : {
2559 : /* try to give a good error message */
2560 : const char *cmdtag;
2561 :
2562 : /* A SELECT without resultDesc must be SELECT INTO */
2563 8 : if (plansource->commandTag == CMDTAG_SELECT)
2564 8 : cmdtag = "SELECT INTO";
2565 : else
2566 0 : cmdtag = GetCommandTagName(plansource->commandTag);
2567 8 : ereport(ERROR,
2568 : (errcode(ERRCODE_SYNTAX_ERROR),
2569 : /* translator: %s is name of a SQL command, eg INSERT */
2570 : errmsg("%s query does not return tuples", cmdtag)));
2571 : }
2572 :
2573 : /*
2574 : * Replan if needed, and increment plan refcount. If it's a saved
2575 : * plan, the refcount must be backed by the plan_owner.
2576 : */
2577 469195 : cplan = GetCachedPlan(plansource, options->params,
2578 469195 : plan_owner, _SPI_current->queryEnv);
2579 :
2580 469106 : stmt_list = cplan->stmt_list;
2581 :
2582 : /*
2583 : * If we weren't given a specific snapshot to use, and the statement
2584 : * list requires a snapshot, set that up.
2585 : */
2586 937448 : if (snapshot == InvalidSnapshot &&
2587 936684 : (list_length(stmt_list) > 1 ||
2588 936684 : (list_length(stmt_list) == 1 &&
2589 468342 : PlannedStmtRequiresSnapshot(linitial_node(PlannedStmt,
2590 : stmt_list)))))
2591 : {
2592 : /*
2593 : * First, ensure there's a Portal-level snapshot. This back-fills
2594 : * the snapshot stack in case the previous operation was a COMMIT
2595 : * or ROLLBACK inside a procedure or DO block. (We can't put back
2596 : * the Portal snapshot any sooner, or we'd break cases like doing
2597 : * SET or LOCK just after COMMIT.) It's enough to check once per
2598 : * statement list, since COMMIT/ROLLBACK/CALL/DO can't appear
2599 : * within a multi-statement list.
2600 : */
2601 459678 : EnsurePortalSnapshotExists();
2602 :
2603 : /*
2604 : * In the default non-read-only case, get a new per-statement-list
2605 : * snapshot, replacing any that we pushed in a previous cycle.
2606 : * Skip it when doing non-atomic execution, though (we rely
2607 : * entirely on the Portal snapshot in that case).
2608 : */
2609 459678 : if (!options->read_only && !allow_nonatomic)
2610 : {
2611 456766 : if (pushed_active_snap)
2612 4 : PopActiveSnapshot();
2613 456766 : PushActiveSnapshot(GetTransactionSnapshot());
2614 456766 : pushed_active_snap = true;
2615 : }
2616 : }
2617 :
2618 934473 : foreach(lc2, stmt_list)
2619 : {
2620 469106 : PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
2621 469106 : bool canSetTag = stmt->canSetTag;
2622 : DestReceiver *dest;
2623 :
2624 : /*
2625 : * Reset output state. (Note that if a non-SPI receiver is used,
2626 : * _SPI_current->processed will stay zero, and that's what we'll
2627 : * report to the caller. It's the receiver's job to count tuples
2628 : * in that case.)
2629 : */
2630 469106 : _SPI_current->processed = 0;
2631 469106 : _SPI_current->tuptable = NULL;
2632 :
2633 : /* Check for unsupported cases. */
2634 469106 : if (stmt->utilityStmt)
2635 : {
2636 16972 : if (IsA(stmt->utilityStmt, CopyStmt))
2637 : {
2638 9 : CopyStmt *cstmt = (CopyStmt *) stmt->utilityStmt;
2639 :
2640 9 : if (cstmt->filename == NULL)
2641 : {
2642 4 : my_res = SPI_ERROR_COPY;
2643 9 : goto fail;
2644 : }
2645 : }
2646 16963 : else if (IsA(stmt->utilityStmt, TransactionStmt))
2647 : {
2648 5 : my_res = SPI_ERROR_TRANSACTION;
2649 5 : goto fail;
2650 : }
2651 : }
2652 :
2653 469097 : if (options->read_only && !CommandIsReadOnly(stmt))
2654 0 : ereport(ERROR,
2655 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2656 : /* translator: %s is a SQL statement name */
2657 : errmsg("%s is not allowed in a non-volatile function",
2658 : CreateCommandName((Node *) stmt))));
2659 :
2660 : /*
2661 : * If not read-only mode, advance the command counter before each
2662 : * command and update the snapshot. (But skip it if the snapshot
2663 : * isn't under our control.)
2664 : */
2665 469097 : if (!options->read_only && pushed_active_snap)
2666 : {
2667 456798 : CommandCounterIncrement();
2668 456798 : UpdateActiveSnapshotCommandId();
2669 : }
2670 :
2671 : /*
2672 : * Select appropriate tuple receiver. Output from non-canSetTag
2673 : * subqueries always goes to the bit bucket.
2674 : */
2675 469097 : if (!canSetTag)
2676 0 : dest = CreateDestReceiver(DestNone);
2677 469097 : else if (options->dest)
2678 1787 : dest = options->dest;
2679 : else
2680 467310 : dest = CreateDestReceiver(DestSPI);
2681 :
2682 469097 : if (stmt->utilityStmt == NULL)
2683 : {
2684 : QueryDesc *qdesc;
2685 : Snapshot snap;
2686 :
2687 452134 : if (ActiveSnapshotSet())
2688 452134 : snap = GetActiveSnapshot();
2689 : else
2690 0 : snap = InvalidSnapshot;
2691 :
2692 452134 : qdesc = CreateQueryDesc(stmt,
2693 : plansource->query_string,
2694 : snap, crosscheck_snapshot,
2695 : dest,
2696 452134 : options->params,
2697 452134 : _SPI_current->queryEnv,
2698 : 0);
2699 452134 : res = _SPI_pquery(qdesc, fire_triggers,
2700 : canSetTag ? options->tcount : 0);
2701 448503 : FreeQueryDesc(qdesc);
2702 : }
2703 : else
2704 : {
2705 : ProcessUtilityContext context;
2706 : QueryCompletion qc;
2707 :
2708 : /*
2709 : * If we're not allowing nonatomic operations, tell
2710 : * ProcessUtility this is an atomic execution context.
2711 : */
2712 16963 : if (allow_nonatomic)
2713 51 : context = PROCESS_UTILITY_QUERY_NONATOMIC;
2714 : else
2715 16912 : context = PROCESS_UTILITY_QUERY;
2716 :
2717 16963 : InitializeQueryCompletion(&qc);
2718 16963 : ProcessUtility(stmt,
2719 : plansource->query_string,
2720 : true, /* protect plancache's node tree */
2721 : context,
2722 16963 : options->params,
2723 16963 : _SPI_current->queryEnv,
2724 : dest,
2725 : &qc);
2726 :
2727 : /* Update "processed" if stmt returned tuples */
2728 16864 : if (_SPI_current->tuptable)
2729 1230 : _SPI_current->processed = _SPI_current->tuptable->numvals;
2730 :
2731 16864 : res = SPI_OK_UTILITY;
2732 :
2733 : /*
2734 : * Some utility statements return a row count, even though the
2735 : * tuples are not returned to the caller.
2736 : */
2737 16864 : if (IsA(stmt->utilityStmt, CreateTableAsStmt))
2738 : {
2739 33 : CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
2740 :
2741 33 : if (qc.commandTag == CMDTAG_SELECT)
2742 29 : _SPI_current->processed = qc.nprocessed;
2743 : else
2744 : {
2745 : /*
2746 : * Must be an IF NOT EXISTS that did nothing, or a
2747 : * CREATE ... WITH NO DATA.
2748 : */
2749 : Assert(ctastmt->if_not_exists ||
2750 : ctastmt->into->skipData);
2751 4 : _SPI_current->processed = 0;
2752 : }
2753 :
2754 : /*
2755 : * For historical reasons, if CREATE TABLE AS was spelled
2756 : * as SELECT INTO, return a special return code.
2757 : */
2758 33 : if (ctastmt->is_select_into)
2759 0 : res = SPI_OK_SELINTO;
2760 : }
2761 16831 : else if (IsA(stmt->utilityStmt, CopyStmt))
2762 : {
2763 : Assert(qc.commandTag == CMDTAG_COPY);
2764 5 : _SPI_current->processed = qc.nprocessed;
2765 : }
2766 : }
2767 :
2768 : /*
2769 : * The last canSetTag query sets the status values returned to the
2770 : * caller. Be careful to free any tuptables not returned, to
2771 : * avoid intra-transaction memory leak.
2772 : */
2773 465367 : if (canSetTag)
2774 : {
2775 465367 : my_processed = _SPI_current->processed;
2776 465367 : SPI_freetuptable(my_tuptable);
2777 465367 : my_tuptable = _SPI_current->tuptable;
2778 465367 : my_res = res;
2779 : }
2780 : else
2781 : {
2782 0 : SPI_freetuptable(_SPI_current->tuptable);
2783 0 : _SPI_current->tuptable = NULL;
2784 : }
2785 :
2786 : /*
2787 : * We don't issue a destroy call to the receiver. The SPI and
2788 : * None receivers would ignore it anyway, while if the caller
2789 : * supplied a receiver, it's not our job to destroy it.
2790 : */
2791 :
2792 465367 : if (res < 0)
2793 : {
2794 0 : my_res = res;
2795 0 : goto fail;
2796 : }
2797 : }
2798 :
2799 : /* Done with this plan, so release refcount */
2800 465367 : ReleaseCachedPlan(cplan, plan_owner);
2801 465367 : cplan = NULL;
2802 :
2803 : /*
2804 : * If not read-only mode, advance the command counter after the last
2805 : * command. This ensures that its effects are visible, in case it was
2806 : * DDL that would affect the next CachedPlanSource.
2807 : */
2808 465367 : if (!options->read_only)
2809 461816 : CommandCounterIncrement();
2810 : }
2811 :
2812 465372 : fail:
2813 :
2814 : /* Pop the snapshot off the stack if we pushed one */
2815 465372 : if (pushed_active_snap)
2816 453857 : PopActiveSnapshot();
2817 :
2818 : /* We no longer need the cached plan refcount, if any */
2819 465372 : if (cplan)
2820 9 : ReleaseCachedPlan(cplan, plan_owner);
2821 :
2822 : /*
2823 : * Pop the error context stack
2824 : */
2825 465372 : error_context_stack = spierrcontext.previous;
2826 :
2827 : /* Save results for caller */
2828 465372 : SPI_processed = my_processed;
2829 465372 : SPI_tuptable = my_tuptable;
2830 :
2831 : /* tuptable now is caller's responsibility, not SPI's */
2832 465372 : _SPI_current->tuptable = NULL;
2833 :
2834 : /*
2835 : * If none of the queries had canSetTag, return SPI_OK_REWRITTEN. Prior to
2836 : * 8.4, we used return the last query's result code, but not its auxiliary
2837 : * results, but that's confusing.
2838 : */
2839 465372 : if (my_res == 0)
2840 0 : my_res = SPI_OK_REWRITTEN;
2841 :
2842 465372 : return my_res;
2843 : }
2844 :
2845 : /*
2846 : * Convert arrays of query parameters to form wanted by planner and executor
2847 : */
2848 : static ParamListInfo
2849 407844 : _SPI_convert_params(int nargs, Oid *argtypes,
2850 : const Datum *Values, const char *Nulls)
2851 : {
2852 : ParamListInfo paramLI;
2853 :
2854 407844 : if (nargs > 0)
2855 : {
2856 406900 : paramLI = makeParamList(nargs);
2857 :
2858 817661 : for (int i = 0; i < nargs; i++)
2859 : {
2860 410761 : ParamExternData *prm = ¶mLI->params[i];
2861 :
2862 410761 : prm->value = Values[i];
2863 410761 : prm->isnull = (Nulls && Nulls[i] == 'n');
2864 410761 : prm->pflags = PARAM_FLAG_CONST;
2865 410761 : prm->ptype = argtypes[i];
2866 : }
2867 : }
2868 : else
2869 944 : paramLI = NULL;
2870 407844 : return paramLI;
2871 : }
2872 :
2873 : static int
2874 452134 : _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount)
2875 : {
2876 452134 : int operation = queryDesc->operation;
2877 : int eflags;
2878 : int res;
2879 :
2880 452134 : switch (operation)
2881 : {
2882 433346 : case CMD_SELECT:
2883 433346 : if (queryDesc->dest->mydest == DestNone)
2884 : {
2885 : /* Don't return SPI_OK_SELECT if we're discarding result */
2886 0 : res = SPI_OK_UTILITY;
2887 : }
2888 : else
2889 433346 : res = SPI_OK_SELECT;
2890 433346 : break;
2891 12044 : case CMD_INSERT:
2892 12044 : if (queryDesc->plannedstmt->hasReturning)
2893 1070 : res = SPI_OK_INSERT_RETURNING;
2894 : else
2895 10974 : res = SPI_OK_INSERT;
2896 12044 : break;
2897 5617 : case CMD_DELETE:
2898 5617 : if (queryDesc->plannedstmt->hasReturning)
2899 0 : res = SPI_OK_DELETE_RETURNING;
2900 : else
2901 5617 : res = SPI_OK_DELETE;
2902 5617 : break;
2903 1083 : case CMD_UPDATE:
2904 1083 : if (queryDesc->plannedstmt->hasReturning)
2905 10 : res = SPI_OK_UPDATE_RETURNING;
2906 : else
2907 1073 : res = SPI_OK_UPDATE;
2908 1083 : break;
2909 44 : case CMD_MERGE:
2910 44 : if (queryDesc->plannedstmt->hasReturning)
2911 12 : res = SPI_OK_MERGE_RETURNING;
2912 : else
2913 32 : res = SPI_OK_MERGE;
2914 44 : break;
2915 0 : default:
2916 0 : return SPI_ERROR_OPUNKNOWN;
2917 : }
2918 :
2919 : #ifdef SPI_EXECUTOR_STATS
2920 : if (ShowExecutorStats)
2921 : ResetUsage();
2922 : #endif
2923 :
2924 : /* Select execution options */
2925 452134 : if (fire_triggers)
2926 47008 : eflags = 0; /* default run-to-completion flags */
2927 : else
2928 405126 : eflags = EXEC_FLAG_SKIP_TRIGGERS;
2929 :
2930 452134 : ExecutorStart(queryDesc, eflags);
2931 :
2932 452134 : ExecutorRun(queryDesc, ForwardScanDirection, tcount);
2933 :
2934 448505 : _SPI_current->processed = queryDesc->estate->es_processed;
2935 :
2936 448505 : if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
2937 430833 : queryDesc->dest->mydest == DestSPI)
2938 : {
2939 429087 : if (_SPI_checktuples())
2940 0 : elog(ERROR, "consistency check on SPI tuple count failed");
2941 : }
2942 :
2943 448505 : ExecutorFinish(queryDesc);
2944 448503 : ExecutorEnd(queryDesc);
2945 : /* FreeQueryDesc is done by the caller */
2946 :
2947 : #ifdef SPI_EXECUTOR_STATS
2948 : if (ShowExecutorStats)
2949 : ShowUsage("SPI EXECUTOR STATS");
2950 : #endif
2951 :
2952 448503 : return res;
2953 : }
2954 :
2955 : /*
2956 : * _SPI_error_callback
2957 : *
2958 : * Add context information when a query invoked via SPI fails
2959 : */
2960 : static void
2961 4378 : _SPI_error_callback(void *arg)
2962 : {
2963 4378 : SPICallbackArg *carg = (SPICallbackArg *) arg;
2964 4378 : const char *query = carg->query;
2965 : int syntaxerrposition;
2966 :
2967 4378 : if (query == NULL) /* in case arg wasn't set yet */
2968 0 : return;
2969 :
2970 : /*
2971 : * If there is a syntax error position, convert to internal syntax error;
2972 : * otherwise treat the query as an item of context stack
2973 : */
2974 4378 : syntaxerrposition = geterrposition();
2975 4378 : if (syntaxerrposition > 0)
2976 : {
2977 61 : errposition(0);
2978 61 : internalerrposition(syntaxerrposition);
2979 61 : internalerrquery(query);
2980 : }
2981 : else
2982 : {
2983 : /* Use the parse mode to decide how to describe the query */
2984 4317 : switch (carg->mode)
2985 : {
2986 52 : case RAW_PARSE_PLPGSQL_EXPR:
2987 52 : errcontext("PL/pgSQL expression \"%s\"", query);
2988 52 : break;
2989 8 : case RAW_PARSE_PLPGSQL_ASSIGN1:
2990 : case RAW_PARSE_PLPGSQL_ASSIGN2:
2991 : case RAW_PARSE_PLPGSQL_ASSIGN3:
2992 8 : errcontext("PL/pgSQL assignment \"%s\"", query);
2993 8 : break;
2994 4257 : default:
2995 4257 : errcontext("SQL statement \"%s\"", query);
2996 4257 : break;
2997 : }
2998 : }
2999 : }
3000 :
3001 : /*
3002 : * _SPI_cursor_operation()
3003 : *
3004 : * Do a FETCH or MOVE in a cursor
3005 : */
3006 : static void
3007 29796 : _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
3008 : DestReceiver *dest)
3009 : {
3010 : uint64 nfetched;
3011 :
3012 : /* Check that the portal is valid */
3013 29796 : if (!PortalIsValid(portal))
3014 0 : elog(ERROR, "invalid portal in SPI cursor operation");
3015 :
3016 : /* Push the SPI stack */
3017 29796 : if (_SPI_begin_call(true) < 0)
3018 0 : elog(ERROR, "SPI cursor operation called while not connected");
3019 :
3020 : /* Reset the SPI result (note we deliberately don't touch lastoid) */
3021 29796 : SPI_processed = 0;
3022 29796 : SPI_tuptable = NULL;
3023 29796 : _SPI_current->processed = 0;
3024 29796 : _SPI_current->tuptable = NULL;
3025 :
3026 : /* Run the cursor */
3027 29796 : nfetched = PortalRunFetch(portal,
3028 : direction,
3029 : count,
3030 : dest);
3031 :
3032 : /*
3033 : * Think not to combine this store with the preceding function call. If
3034 : * the portal contains calls to functions that use SPI, then _SPI_stack is
3035 : * likely to move around while the portal runs. When control returns,
3036 : * _SPI_current will point to the correct stack entry... but the pointer
3037 : * may be different than it was beforehand. So we must be sure to re-fetch
3038 : * the pointer after the function call completes.
3039 : */
3040 29787 : _SPI_current->processed = nfetched;
3041 :
3042 29787 : if (dest->mydest == DestSPI && _SPI_checktuples())
3043 0 : elog(ERROR, "consistency check on SPI tuple count failed");
3044 :
3045 : /* Put the result into place for access by caller */
3046 29787 : SPI_processed = _SPI_current->processed;
3047 29787 : SPI_tuptable = _SPI_current->tuptable;
3048 :
3049 : /* tuptable now is caller's responsibility, not SPI's */
3050 29787 : _SPI_current->tuptable = NULL;
3051 :
3052 : /* Pop the SPI stack */
3053 29787 : _SPI_end_call(true);
3054 29787 : }
3055 :
3056 :
3057 : static MemoryContext
3058 534000 : _SPI_execmem(void)
3059 : {
3060 534000 : return MemoryContextSwitchTo(_SPI_current->execCxt);
3061 : }
3062 :
3063 : static MemoryContext
3064 993776 : _SPI_procmem(void)
3065 : {
3066 993776 : return MemoryContextSwitchTo(_SPI_current->procCxt);
3067 : }
3068 :
3069 : /*
3070 : * _SPI_begin_call: begin a SPI operation within a connected procedure
3071 : *
3072 : * use_exec is true if we intend to make use of the procedure's execCxt
3073 : * during this SPI operation. We'll switch into that context, and arrange
3074 : * for it to be cleaned up at _SPI_end_call or if an error occurs.
3075 : */
3076 : static int
3077 999055 : _SPI_begin_call(bool use_exec)
3078 : {
3079 999055 : if (_SPI_current == NULL)
3080 0 : return SPI_ERROR_UNCONNECTED;
3081 :
3082 999055 : if (use_exec)
3083 : {
3084 : /* remember when the Executor operation started */
3085 534000 : _SPI_current->execSubid = GetCurrentSubTransactionId();
3086 : /* switch to the Executor memory context */
3087 534000 : _SPI_execmem();
3088 : }
3089 :
3090 999055 : return 0;
3091 : }
3092 :
3093 : /*
3094 : * _SPI_end_call: end a SPI operation within a connected procedure
3095 : *
3096 : * use_exec must be the same as in the previous _SPI_begin_call
3097 : *
3098 : * Note: this currently has no failure return cases, so callers don't check
3099 : */
3100 : static int
3101 530565 : _SPI_end_call(bool use_exec)
3102 : {
3103 530565 : if (use_exec)
3104 : {
3105 : /* switch to the procedure memory context */
3106 530087 : _SPI_procmem();
3107 : /* mark Executor context no longer in use */
3108 530087 : _SPI_current->execSubid = InvalidSubTransactionId;
3109 : /* and free Executor memory */
3110 530087 : MemoryContextReset(_SPI_current->execCxt);
3111 : }
3112 :
3113 530565 : return 0;
3114 : }
3115 :
3116 : static bool
3117 458846 : _SPI_checktuples(void)
3118 : {
3119 458846 : uint64 processed = _SPI_current->processed;
3120 458846 : SPITupleTable *tuptable = _SPI_current->tuptable;
3121 458846 : bool failed = false;
3122 :
3123 458846 : if (tuptable == NULL) /* spi_dest_startup was not called */
3124 0 : failed = true;
3125 458846 : else if (processed != tuptable->numvals)
3126 0 : failed = true;
3127 :
3128 458846 : return failed;
3129 : }
3130 :
3131 : /*
3132 : * Convert a "temporary" SPIPlan into an "unsaved" plan.
3133 : *
3134 : * The passed _SPI_plan struct is on the stack, and all its subsidiary data
3135 : * is in or under the current SPI executor context. Copy the plan into the
3136 : * SPI procedure context so it will survive _SPI_end_call(). To minimize
3137 : * data copying, this destructively modifies the input plan, by taking the
3138 : * plancache entries away from it and reparenting them to the new SPIPlan.
3139 : */
3140 : static SPIPlanPtr
3141 20287 : _SPI_make_plan_non_temp(SPIPlanPtr plan)
3142 : {
3143 : SPIPlanPtr newplan;
3144 20287 : MemoryContext parentcxt = _SPI_current->procCxt;
3145 : MemoryContext plancxt;
3146 : MemoryContext oldcxt;
3147 : ListCell *lc;
3148 :
3149 : /* Assert the input is a temporary SPIPlan */
3150 : Assert(plan->magic == _SPI_PLAN_MAGIC);
3151 : Assert(plan->plancxt == NULL);
3152 : /* One-shot plans can't be saved */
3153 : Assert(!plan->oneshot);
3154 :
3155 : /*
3156 : * Create a memory context for the plan, underneath the procedure context.
3157 : * We don't expect the plan to be very large.
3158 : */
3159 20287 : plancxt = AllocSetContextCreate(parentcxt,
3160 : "SPI Plan",
3161 : ALLOCSET_SMALL_SIZES);
3162 20287 : oldcxt = MemoryContextSwitchTo(plancxt);
3163 :
3164 : /* Copy the _SPI_plan struct and subsidiary data into the new context */
3165 20287 : newplan = palloc0_object(_SPI_plan);
3166 20287 : newplan->magic = _SPI_PLAN_MAGIC;
3167 20287 : newplan->plancxt = plancxt;
3168 20287 : newplan->parse_mode = plan->parse_mode;
3169 20287 : newplan->cursor_options = plan->cursor_options;
3170 20287 : newplan->nargs = plan->nargs;
3171 20287 : if (plan->nargs > 0)
3172 : {
3173 2333 : newplan->argtypes = palloc_array(Oid, plan->nargs);
3174 2333 : memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
3175 : }
3176 : else
3177 17954 : newplan->argtypes = NULL;
3178 20287 : newplan->parserSetup = plan->parserSetup;
3179 20287 : newplan->parserSetupArg = plan->parserSetupArg;
3180 :
3181 : /*
3182 : * Reparent all the CachedPlanSources into the procedure context. In
3183 : * theory this could fail partway through due to the pallocs, but we don't
3184 : * care too much since both the procedure context and the executor context
3185 : * would go away on error.
3186 : */
3187 40574 : foreach(lc, plan->plancache_list)
3188 : {
3189 20287 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3190 :
3191 20287 : CachedPlanSetParentContext(plansource, parentcxt);
3192 :
3193 : /* Build new list, with list cells in plancxt */
3194 20287 : newplan->plancache_list = lappend(newplan->plancache_list, plansource);
3195 : }
3196 :
3197 20287 : MemoryContextSwitchTo(oldcxt);
3198 :
3199 : /* For safety, unlink the CachedPlanSources from the temporary plan */
3200 20287 : plan->plancache_list = NIL;
3201 :
3202 20287 : return newplan;
3203 : }
3204 :
3205 : /*
3206 : * Make a "saved" copy of the given plan.
3207 : */
3208 : static SPIPlanPtr
3209 0 : _SPI_save_plan(SPIPlanPtr plan)
3210 : {
3211 : SPIPlanPtr newplan;
3212 : MemoryContext plancxt;
3213 : MemoryContext oldcxt;
3214 : ListCell *lc;
3215 :
3216 : /* One-shot plans can't be saved */
3217 : Assert(!plan->oneshot);
3218 :
3219 : /*
3220 : * Create a memory context for the plan. We don't expect the plan to be
3221 : * very large, so use smaller-than-default alloc parameters. It's a
3222 : * transient context until we finish copying everything.
3223 : */
3224 0 : plancxt = AllocSetContextCreate(CurrentMemoryContext,
3225 : "SPI Plan",
3226 : ALLOCSET_SMALL_SIZES);
3227 0 : oldcxt = MemoryContextSwitchTo(plancxt);
3228 :
3229 : /* Copy the SPI plan into its own context */
3230 0 : newplan = palloc0_object(_SPI_plan);
3231 0 : newplan->magic = _SPI_PLAN_MAGIC;
3232 0 : newplan->plancxt = plancxt;
3233 0 : newplan->parse_mode = plan->parse_mode;
3234 0 : newplan->cursor_options = plan->cursor_options;
3235 0 : newplan->nargs = plan->nargs;
3236 0 : if (plan->nargs > 0)
3237 : {
3238 0 : newplan->argtypes = palloc_array(Oid, plan->nargs);
3239 0 : memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
3240 : }
3241 : else
3242 0 : newplan->argtypes = NULL;
3243 0 : newplan->parserSetup = plan->parserSetup;
3244 0 : newplan->parserSetupArg = plan->parserSetupArg;
3245 :
3246 : /* Copy all the plancache entries */
3247 0 : foreach(lc, plan->plancache_list)
3248 : {
3249 0 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3250 : CachedPlanSource *newsource;
3251 :
3252 0 : newsource = CopyCachedPlan(plansource);
3253 0 : newplan->plancache_list = lappend(newplan->plancache_list, newsource);
3254 : }
3255 :
3256 0 : MemoryContextSwitchTo(oldcxt);
3257 :
3258 : /*
3259 : * Mark it saved, reparent it under CacheMemoryContext, and mark all the
3260 : * component CachedPlanSources as saved. This sequence cannot fail
3261 : * partway through, so there's no risk of long-term memory leakage.
3262 : */
3263 0 : newplan->saved = true;
3264 0 : MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
3265 :
3266 0 : foreach(lc, newplan->plancache_list)
3267 : {
3268 0 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3269 :
3270 0 : SaveCachedPlan(plansource);
3271 : }
3272 :
3273 0 : return newplan;
3274 : }
3275 :
3276 : /*
3277 : * Internal lookup of ephemeral named relation by name.
3278 : */
3279 : static EphemeralNamedRelation
3280 478 : _SPI_find_ENR_by_name(const char *name)
3281 : {
3282 : /* internal static function; any error is bug in SPI itself */
3283 : Assert(name != NULL);
3284 :
3285 : /* fast exit if no tuplestores have been added */
3286 478 : if (_SPI_current->queryEnv == NULL)
3287 379 : return NULL;
3288 :
3289 99 : return get_ENR(_SPI_current->queryEnv, name);
3290 : }
3291 :
3292 : /*
3293 : * Register an ephemeral named relation for use by the planner and executor on
3294 : * subsequent calls using this SPI connection.
3295 : */
3296 : int
3297 478 : SPI_register_relation(EphemeralNamedRelation enr)
3298 : {
3299 : EphemeralNamedRelation match;
3300 : int res;
3301 :
3302 478 : if (enr == NULL || enr->md.name == NULL)
3303 0 : return SPI_ERROR_ARGUMENT;
3304 :
3305 478 : res = _SPI_begin_call(false); /* keep current memory context */
3306 478 : if (res < 0)
3307 0 : return res;
3308 :
3309 478 : match = _SPI_find_ENR_by_name(enr->md.name);
3310 478 : if (match)
3311 0 : res = SPI_ERROR_REL_DUPLICATE;
3312 : else
3313 : {
3314 478 : if (_SPI_current->queryEnv == NULL)
3315 379 : _SPI_current->queryEnv = create_queryEnv();
3316 :
3317 478 : register_ENR(_SPI_current->queryEnv, enr);
3318 478 : res = SPI_OK_REL_REGISTER;
3319 : }
3320 :
3321 478 : _SPI_end_call(false);
3322 :
3323 478 : return res;
3324 : }
3325 :
3326 : /*
3327 : * Unregister an ephemeral named relation by name. This will probably be a
3328 : * rarely used function, since SPI_finish will clear it automatically.
3329 : */
3330 : int
3331 0 : SPI_unregister_relation(const char *name)
3332 : {
3333 : EphemeralNamedRelation match;
3334 : int res;
3335 :
3336 0 : if (name == NULL)
3337 0 : return SPI_ERROR_ARGUMENT;
3338 :
3339 0 : res = _SPI_begin_call(false); /* keep current memory context */
3340 0 : if (res < 0)
3341 0 : return res;
3342 :
3343 0 : match = _SPI_find_ENR_by_name(name);
3344 0 : if (match)
3345 : {
3346 0 : unregister_ENR(_SPI_current->queryEnv, match->md.name);
3347 0 : res = SPI_OK_REL_UNREGISTER;
3348 : }
3349 : else
3350 0 : res = SPI_ERROR_REL_NOT_FOUND;
3351 :
3352 0 : _SPI_end_call(false);
3353 :
3354 0 : return res;
3355 : }
3356 :
3357 : /*
3358 : * Register the transient relations from 'tdata' using this SPI connection.
3359 : * This should be called by PL implementations' trigger handlers after
3360 : * connecting, in order to make transition tables visible to any queries run
3361 : * in this connection.
3362 : */
3363 : int
3364 10165 : SPI_register_trigger_data(TriggerData *tdata)
3365 : {
3366 10165 : if (tdata == NULL)
3367 0 : return SPI_ERROR_ARGUMENT;
3368 :
3369 10165 : if (tdata->tg_newtable)
3370 : {
3371 : EphemeralNamedRelation enr =
3372 271 : palloc_object(EphemeralNamedRelationData);
3373 : int rc;
3374 :
3375 271 : enr->md.name = tdata->tg_trigger->tgnewtable;
3376 271 : enr->md.reliddesc = tdata->tg_relation->rd_id;
3377 271 : enr->md.tupdesc = NULL;
3378 271 : enr->md.enrtype = ENR_NAMED_TUPLESTORE;
3379 271 : enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_newtable);
3380 271 : enr->reldata = tdata->tg_newtable;
3381 271 : rc = SPI_register_relation(enr);
3382 271 : if (rc != SPI_OK_REL_REGISTER)
3383 0 : return rc;
3384 : }
3385 :
3386 10165 : if (tdata->tg_oldtable)
3387 : {
3388 : EphemeralNamedRelation enr =
3389 207 : palloc_object(EphemeralNamedRelationData);
3390 : int rc;
3391 :
3392 207 : enr->md.name = tdata->tg_trigger->tgoldtable;
3393 207 : enr->md.reliddesc = tdata->tg_relation->rd_id;
3394 207 : enr->md.tupdesc = NULL;
3395 207 : enr->md.enrtype = ENR_NAMED_TUPLESTORE;
3396 207 : enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_oldtable);
3397 207 : enr->reldata = tdata->tg_oldtable;
3398 207 : rc = SPI_register_relation(enr);
3399 207 : if (rc != SPI_OK_REL_REGISTER)
3400 0 : return rc;
3401 : }
3402 :
3403 10165 : return SPI_OK_TD_REGISTER;
3404 : }
|