Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * spi.c
4 : * Server Programming Interface
5 : *
6 : * Portions Copyright (c) 1996-2025, 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/typcache.h"
36 :
37 :
38 : /*
39 : * These global variables are part of the API for various SPI functions
40 : * (a horrible API choice, but it's too late now). To reduce the risk of
41 : * interference between different SPI callers, we save and restore them
42 : * when entering/exiting a SPI nesting level.
43 : */
44 : uint64 SPI_processed = 0;
45 : SPITupleTable *SPI_tuptable = NULL;
46 : int SPI_result = 0;
47 :
48 : static _SPI_connection *_SPI_stack = NULL;
49 : static _SPI_connection *_SPI_current = NULL;
50 : static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
51 : static int _SPI_connected = -1; /* current stack index */
52 :
53 : typedef struct SPICallbackArg
54 : {
55 : const char *query;
56 : RawParseMode mode;
57 : } SPICallbackArg;
58 :
59 : static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
60 : ParamListInfo paramLI, bool read_only);
61 :
62 : static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
63 :
64 : static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
65 :
66 : static int _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
67 : Snapshot snapshot, Snapshot crosscheck_snapshot,
68 : bool fire_triggers);
69 :
70 : static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
71 : Datum *Values, const char *Nulls);
72 :
73 : static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount,
74 : CachedPlanSource *plansource, int query_index);
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 17304 : SPI_connect(void)
96 : {
97 17304 : return SPI_connect_ext(0);
98 : }
99 :
100 : int
101 104352 : SPI_connect_ext(int options)
102 : {
103 : int newdepth;
104 :
105 : /* Enlarge stack if necessary */
106 104352 : if (_SPI_stack == NULL)
107 : {
108 1754 : if (_SPI_connected != -1 || _SPI_stack_depth != 0)
109 0 : elog(ERROR, "SPI stack corrupted");
110 1754 : newdepth = 16;
111 1754 : _SPI_stack = (_SPI_connection *)
112 1754 : MemoryContextAlloc(TopMemoryContext,
113 : newdepth * sizeof(_SPI_connection));
114 1754 : _SPI_stack_depth = newdepth;
115 : }
116 : else
117 : {
118 102598 : if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
119 0 : elog(ERROR, "SPI stack corrupted");
120 102598 : if (_SPI_stack_depth == _SPI_connected + 1)
121 : {
122 26 : newdepth = _SPI_stack_depth * 2;
123 26 : _SPI_stack = (_SPI_connection *)
124 26 : repalloc(_SPI_stack,
125 : newdepth * sizeof(_SPI_connection));
126 26 : _SPI_stack_depth = newdepth;
127 : }
128 : }
129 :
130 : /* Enter new stack level */
131 104352 : _SPI_connected++;
132 : Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
133 :
134 104352 : _SPI_current = &(_SPI_stack[_SPI_connected]);
135 104352 : _SPI_current->processed = 0;
136 104352 : _SPI_current->tuptable = NULL;
137 104352 : _SPI_current->execSubid = InvalidSubTransactionId;
138 104352 : slist_init(&_SPI_current->tuptables);
139 104352 : _SPI_current->procCxt = NULL; /* in case we fail to create 'em */
140 104352 : _SPI_current->execCxt = NULL;
141 104352 : _SPI_current->connectSubid = GetCurrentSubTransactionId();
142 104352 : _SPI_current->queryEnv = NULL;
143 104352 : _SPI_current->atomic = (options & SPI_OPT_NONATOMIC ? false : true);
144 104352 : _SPI_current->internal_xact = false;
145 104352 : _SPI_current->outer_processed = SPI_processed;
146 104352 : _SPI_current->outer_tuptable = SPI_tuptable;
147 104352 : _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 104352 : _SPI_current->procCxt = AllocSetContextCreate(_SPI_current->atomic ? TopTransactionContext : PortalContext,
163 : "SPI Proc",
164 : ALLOCSET_DEFAULT_SIZES);
165 104352 : _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 104352 : _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 104352 : SPI_processed = 0;
176 104352 : SPI_tuptable = NULL;
177 104352 : SPI_result = 0;
178 :
179 104352 : return SPI_OK_CONNECT;
180 : }
181 :
182 : int
183 101480 : SPI_finish(void)
184 : {
185 : int res;
186 :
187 101480 : res = _SPI_begin_call(false); /* just check we're connected */
188 101480 : if (res < 0)
189 0 : return res;
190 :
191 : /* Restore memory context as it was before procedure call */
192 101480 : MemoryContextSwitchTo(_SPI_current->savedcxt);
193 :
194 : /* Release memory used in procedure call (including tuptables) */
195 101480 : MemoryContextDelete(_SPI_current->execCxt);
196 101480 : _SPI_current->execCxt = NULL;
197 101480 : MemoryContextDelete(_SPI_current->procCxt);
198 101480 : _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 101480 : SPI_processed = _SPI_current->outer_processed;
205 101480 : SPI_tuptable = _SPI_current->outer_tuptable;
206 101480 : SPI_result = _SPI_current->outer_result;
207 :
208 : /* Exit stack level */
209 101480 : _SPI_connected--;
210 101480 : if (_SPI_connected < 0)
211 87358 : _SPI_current = NULL;
212 : else
213 14122 : _SPI_current = &(_SPI_stack[_SPI_connected]);
214 :
215 101480 : 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 4288 : _SPI_commit(bool chain)
229 : {
230 4288 : 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 4288 : if (_SPI_current->atomic)
240 32 : 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 4256 : if (IsSubTransaction())
255 6 : ereport(ERROR,
256 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
257 : errmsg("cannot commit while a subtransaction is active")));
258 :
259 4250 : if (chain)
260 4 : SaveTransactionCharacteristics(&savetc);
261 :
262 : /* Catch any error occurring during the COMMIT */
263 4250 : PG_TRY();
264 : {
265 : /* Protect current SPI stack entry against deletion */
266 4250 : _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 4250 : HoldPinnedPortals();
274 :
275 : /* Release snapshots associated with portals */
276 4248 : ForgetPortalSnapshots();
277 :
278 : /* Do the deed */
279 4248 : CommitTransactionCommand();
280 :
281 : /* Immediately start a new transaction */
282 4234 : StartTransactionCommand();
283 4234 : if (chain)
284 4 : RestoreTransactionCharacteristics(&savetc);
285 :
286 4234 : MemoryContextSwitchTo(oldcontext);
287 :
288 4234 : _SPI_current->internal_xact = false;
289 : }
290 16 : PG_CATCH();
291 : {
292 : ErrorData *edata;
293 :
294 : /* Save error info in caller's context */
295 16 : MemoryContextSwitchTo(oldcontext);
296 16 : edata = CopyErrorData();
297 16 : 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 16 : AbortCurrentTransaction();
304 :
305 : /* ... and start a new one */
306 16 : StartTransactionCommand();
307 16 : if (chain)
308 0 : RestoreTransactionCharacteristics(&savetc);
309 :
310 16 : MemoryContextSwitchTo(oldcontext);
311 :
312 16 : _SPI_current->internal_xact = false;
313 :
314 : /* Now that we've cleaned up the transaction, re-throw the error */
315 16 : ReThrowError(edata);
316 : }
317 4234 : PG_END_TRY();
318 4234 : }
319 :
320 : void
321 4284 : SPI_commit(void)
322 : {
323 4284 : _SPI_commit(false);
324 4230 : }
325 :
326 : void
327 4 : SPI_commit_and_chain(void)
328 : {
329 4 : _SPI_commit(true);
330 4 : }
331 :
332 : static void
333 180 : _SPI_rollback(bool chain)
334 : {
335 180 : MemoryContext oldcontext = CurrentMemoryContext;
336 : SavedTransactionCharacteristics savetc;
337 :
338 : /* see comments in _SPI_commit() */
339 180 : 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 180 : if (IsSubTransaction())
346 4 : ereport(ERROR,
347 : (errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
348 : errmsg("cannot roll back while a subtransaction is active")));
349 :
350 176 : if (chain)
351 4 : SaveTransactionCharacteristics(&savetc);
352 :
353 : /* Catch any error occurring during the ROLLBACK */
354 176 : PG_TRY();
355 : {
356 : /* Protect current SPI stack entry against deletion */
357 176 : _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 176 : HoldPinnedPortals();
366 :
367 : /* Release snapshots associated with portals */
368 172 : ForgetPortalSnapshots();
369 :
370 : /* Do the deed */
371 172 : AbortCurrentTransaction();
372 :
373 : /* Immediately start a new transaction */
374 172 : StartTransactionCommand();
375 172 : if (chain)
376 4 : RestoreTransactionCharacteristics(&savetc);
377 :
378 172 : MemoryContextSwitchTo(oldcontext);
379 :
380 172 : _SPI_current->internal_xact = false;
381 : }
382 4 : PG_CATCH();
383 : {
384 : ErrorData *edata;
385 :
386 : /* Save error info in caller's context */
387 4 : MemoryContextSwitchTo(oldcontext);
388 4 : edata = CopyErrorData();
389 4 : 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 4 : AbortCurrentTransaction();
397 :
398 : /* ... and start a new one */
399 4 : StartTransactionCommand();
400 4 : if (chain)
401 0 : RestoreTransactionCharacteristics(&savetc);
402 :
403 4 : MemoryContextSwitchTo(oldcontext);
404 :
405 4 : _SPI_current->internal_xact = false;
406 :
407 : /* Now that we've cleaned up the transaction, re-throw the error */
408 4 : ReThrowError(edata);
409 : }
410 172 : PG_END_TRY();
411 172 : }
412 :
413 : void
414 176 : SPI_rollback(void)
415 : {
416 176 : _SPI_rollback(false);
417 168 : }
418 :
419 : void
420 4 : SPI_rollback_and_chain(void)
421 : {
422 4 : _SPI_rollback(true);
423 4 : }
424 :
425 : /*
426 : * Clean up SPI state at transaction commit or abort.
427 : */
428 : void
429 801234 : AtEOXact_SPI(bool isCommit)
430 : {
431 801234 : 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 803884 : while (_SPI_connected >= 0)
438 : {
439 7076 : _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
440 :
441 7076 : if (connection->internal_xact)
442 4426 : break;
443 :
444 2650 : 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 2650 : SPI_processed = connection->outer_processed;
458 2650 : SPI_tuptable = connection->outer_tuptable;
459 2650 : SPI_result = connection->outer_result;
460 :
461 2650 : _SPI_connected--;
462 2650 : if (_SPI_connected < 0)
463 2576 : _SPI_current = NULL;
464 : else
465 74 : _SPI_current = &(_SPI_stack[_SPI_connected]);
466 : }
467 :
468 : /* We should only find entries to pop during an ABORT. */
469 801234 : 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 801234 : }
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 19970 : AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
484 : {
485 19970 : bool found = false;
486 :
487 20192 : while (_SPI_connected >= 0)
488 : {
489 15696 : _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
490 :
491 15696 : if (connection->connectSubid != mySubid)
492 15474 : break; /* couldn't be any underneath it either */
493 :
494 222 : if (connection->internal_xact)
495 0 : break;
496 :
497 222 : found = true;
498 :
499 : /*
500 : * Release procedure memory explicitly (see note in SPI_connect)
501 : */
502 222 : if (connection->execCxt)
503 : {
504 222 : MemoryContextDelete(connection->execCxt);
505 222 : connection->execCxt = NULL;
506 : }
507 222 : if (connection->procCxt)
508 : {
509 222 : MemoryContextDelete(connection->procCxt);
510 222 : 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 222 : SPI_processed = connection->outer_processed;
519 222 : SPI_tuptable = connection->outer_tuptable;
520 222 : SPI_result = connection->outer_result;
521 :
522 222 : _SPI_connected--;
523 222 : if (_SPI_connected < 0)
524 72 : _SPI_current = NULL;
525 : else
526 150 : _SPI_current = &(_SPI_stack[_SPI_connected]);
527 : }
528 :
529 19970 : 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 19970 : 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 6330 : if (_SPI_current->execSubid >= mySubid)
548 : {
549 5540 : _SPI_current->execSubid = InvalidSubTransactionId;
550 5540 : MemoryContextReset(_SPI_current->execCxt);
551 : }
552 :
553 : /* throw away any tuple tables created within current subxact */
554 15344 : slist_foreach_modify(siter, &_SPI_current->tuptables)
555 : {
556 : SPITupleTable *tuptable;
557 :
558 9014 : tuptable = slist_container(SPITupleTable, next, siter.cur);
559 9014 : 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 5306 : slist_delete_current(&siter);
568 5306 : if (tuptable == _SPI_current->tuptable)
569 5300 : _SPI_current->tuptable = NULL;
570 5306 : if (tuptable == SPI_tuptable)
571 6 : SPI_tuptable = NULL;
572 5306 : MemoryContextDelete(tuptable->tuptabcxt);
573 : }
574 : }
575 : }
576 19970 : }
577 :
578 : /*
579 : * Are we executing inside a procedure (that is, a nonatomic SPI context)?
580 : */
581 : bool
582 793086 : SPI_inside_nonatomic_context(void)
583 : {
584 793086 : if (_SPI_current == NULL)
585 788660 : return false; /* not in any SPI context at all */
586 : /* these tests must match _SPI_commit's opinion of what's atomic: */
587 4426 : if (_SPI_current->atomic)
588 0 : return false; /* it's atomic (ie function not procedure) */
589 4426 : if (IsSubTransaction())
590 0 : return false; /* if within subtransaction, it's atomic */
591 4426 : return true;
592 : }
593 :
594 :
595 : /* Parse, plan, and execute a query string */
596 : int
597 1494 : SPI_execute(const char *src, bool read_only, long tcount)
598 : {
599 : _SPI_plan plan;
600 : SPIExecuteOptions options;
601 : int res;
602 :
603 1494 : if (src == NULL || tcount < 0)
604 0 : return SPI_ERROR_ARGUMENT;
605 :
606 1494 : res = _SPI_begin_call(true);
607 1494 : if (res < 0)
608 0 : return res;
609 :
610 1494 : memset(&plan, 0, sizeof(_SPI_plan));
611 1494 : plan.magic = _SPI_PLAN_MAGIC;
612 1494 : plan.parse_mode = RAW_PARSE_DEFAULT;
613 1494 : plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
614 :
615 1494 : _SPI_prepare_oneshot_plan(src, &plan);
616 :
617 1478 : memset(&options, 0, sizeof(options));
618 1478 : options.read_only = read_only;
619 1478 : options.tcount = tcount;
620 :
621 1478 : res = _SPI_execute_plan(&plan, &options,
622 : InvalidSnapshot, InvalidSnapshot,
623 : true);
624 :
625 1424 : _SPI_end_call(true);
626 1424 : return res;
627 : }
628 :
629 : /* Obsolete version of SPI_execute */
630 : int
631 530 : SPI_exec(const char *src, long tcount)
632 : {
633 530 : return SPI_execute(src, false, tcount);
634 : }
635 :
636 : /* Parse, plan, and execute a query string, with extensible options */
637 : int
638 14828 : SPI_execute_extended(const char *src,
639 : const SPIExecuteOptions *options)
640 : {
641 : int res;
642 : _SPI_plan plan;
643 :
644 14828 : if (src == NULL || options == NULL)
645 0 : return SPI_ERROR_ARGUMENT;
646 :
647 14828 : res = _SPI_begin_call(true);
648 14828 : if (res < 0)
649 0 : return res;
650 :
651 14828 : memset(&plan, 0, sizeof(_SPI_plan));
652 14828 : plan.magic = _SPI_PLAN_MAGIC;
653 14828 : plan.parse_mode = RAW_PARSE_DEFAULT;
654 14828 : plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
655 14828 : if (options->params)
656 : {
657 570 : plan.parserSetup = options->params->parserSetup;
658 570 : plan.parserSetupArg = options->params->parserSetupArg;
659 : }
660 :
661 14828 : _SPI_prepare_oneshot_plan(src, &plan);
662 :
663 14828 : res = _SPI_execute_plan(&plan, options,
664 : InvalidSnapshot, InvalidSnapshot,
665 : true);
666 :
667 14626 : _SPI_end_call(true);
668 14626 : return res;
669 : }
670 :
671 : /* Execute a previously prepared plan */
672 : int
673 4582 : SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
674 : bool read_only, long tcount)
675 : {
676 : SPIExecuteOptions options;
677 : int res;
678 :
679 4582 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
680 0 : return SPI_ERROR_ARGUMENT;
681 :
682 4582 : if (plan->nargs > 0 && Values == NULL)
683 0 : return SPI_ERROR_PARAM;
684 :
685 4582 : res = _SPI_begin_call(true);
686 4582 : if (res < 0)
687 0 : return res;
688 :
689 4582 : memset(&options, 0, sizeof(options));
690 4582 : options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
691 : Values, Nulls);
692 4582 : options.read_only = read_only;
693 4582 : options.tcount = tcount;
694 :
695 4582 : res = _SPI_execute_plan(plan, &options,
696 : InvalidSnapshot, InvalidSnapshot,
697 : true);
698 :
699 4570 : _SPI_end_call(true);
700 4570 : return res;
701 : }
702 :
703 : /* Obsolete version of SPI_execute_plan */
704 : int
705 186 : SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
706 : {
707 186 : return SPI_execute_plan(plan, Values, Nulls, false, tcount);
708 : }
709 :
710 : /* Execute a previously prepared plan */
711 : int
712 2538 : SPI_execute_plan_extended(SPIPlanPtr plan,
713 : const SPIExecuteOptions *options)
714 : {
715 : int res;
716 :
717 2538 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || options == NULL)
718 0 : return SPI_ERROR_ARGUMENT;
719 :
720 2538 : res = _SPI_begin_call(true);
721 2538 : if (res < 0)
722 0 : return res;
723 :
724 2538 : res = _SPI_execute_plan(plan, options,
725 : InvalidSnapshot, InvalidSnapshot,
726 : true);
727 :
728 2530 : _SPI_end_call(true);
729 2530 : return res;
730 : }
731 :
732 : /* Execute a previously prepared plan */
733 : int
734 78042 : SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
735 : bool read_only, long tcount)
736 : {
737 : SPIExecuteOptions options;
738 : int res;
739 :
740 78042 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
741 0 : return SPI_ERROR_ARGUMENT;
742 :
743 78042 : res = _SPI_begin_call(true);
744 78042 : if (res < 0)
745 0 : return res;
746 :
747 78042 : memset(&options, 0, sizeof(options));
748 78042 : options.params = params;
749 78042 : options.read_only = read_only;
750 78042 : options.tcount = tcount;
751 :
752 78042 : res = _SPI_execute_plan(plan, &options,
753 : InvalidSnapshot, InvalidSnapshot,
754 : true);
755 :
756 72666 : _SPI_end_call(true);
757 72666 : 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 7852 : SPI_execute_snapshot(SPIPlanPtr plan,
775 : 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 7852 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
783 0 : return SPI_ERROR_ARGUMENT;
784 :
785 7852 : if (plan->nargs > 0 && Values == NULL)
786 0 : return SPI_ERROR_PARAM;
787 :
788 7852 : res = _SPI_begin_call(true);
789 7852 : if (res < 0)
790 0 : return res;
791 :
792 7852 : memset(&options, 0, sizeof(options));
793 7852 : options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
794 : Values, Nulls);
795 7852 : options.read_only = read_only;
796 7852 : options.tcount = tcount;
797 :
798 7852 : res = _SPI_execute_plan(plan, &options,
799 : snapshot, crosscheck_snapshot,
800 : fire_triggers);
801 :
802 7838 : _SPI_end_call(true);
803 7838 : 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 : 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 5258 : SPI_prepare(const char *src, int nargs, Oid *argtypes)
862 : {
863 5258 : return SPI_prepare_cursor(src, nargs, argtypes, 0);
864 : }
865 :
866 : SPIPlanPtr
867 5258 : SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
868 : int cursorOptions)
869 : {
870 : _SPI_plan plan;
871 : SPIPlanPtr result;
872 :
873 5258 : if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
874 : {
875 0 : SPI_result = SPI_ERROR_ARGUMENT;
876 0 : return NULL;
877 : }
878 :
879 5258 : SPI_result = _SPI_begin_call(true);
880 5258 : if (SPI_result < 0)
881 0 : return NULL;
882 :
883 5258 : memset(&plan, 0, sizeof(_SPI_plan));
884 5258 : plan.magic = _SPI_PLAN_MAGIC;
885 5258 : plan.parse_mode = RAW_PARSE_DEFAULT;
886 5258 : plan.cursor_options = cursorOptions;
887 5258 : plan.nargs = nargs;
888 5258 : plan.argtypes = argtypes;
889 5258 : plan.parserSetup = NULL;
890 5258 : plan.parserSetupArg = NULL;
891 :
892 5258 : _SPI_prepare_plan(src, &plan);
893 :
894 : /* copy plan to procedure context */
895 5256 : result = _SPI_make_plan_non_temp(&plan);
896 :
897 5256 : _SPI_end_call(true);
898 :
899 5256 : return result;
900 : }
901 :
902 : SPIPlanPtr
903 28040 : SPI_prepare_extended(const char *src,
904 : const SPIPrepareOptions *options)
905 : {
906 : _SPI_plan plan;
907 : SPIPlanPtr result;
908 :
909 28040 : if (src == NULL || options == NULL)
910 : {
911 0 : SPI_result = SPI_ERROR_ARGUMENT;
912 0 : return NULL;
913 : }
914 :
915 28040 : SPI_result = _SPI_begin_call(true);
916 28040 : if (SPI_result < 0)
917 0 : return NULL;
918 :
919 28040 : memset(&plan, 0, sizeof(_SPI_plan));
920 28040 : plan.magic = _SPI_PLAN_MAGIC;
921 28040 : plan.parse_mode = options->parseMode;
922 28040 : plan.cursor_options = options->cursorOptions;
923 28040 : plan.nargs = 0;
924 28040 : plan.argtypes = NULL;
925 28040 : plan.parserSetup = options->parserSetup;
926 28040 : plan.parserSetupArg = options->parserSetupArg;
927 :
928 28040 : _SPI_prepare_plan(src, &plan);
929 :
930 : /* copy plan to procedure context */
931 27946 : result = _SPI_make_plan_non_temp(&plan);
932 :
933 27946 : _SPI_end_call(true);
934 :
935 27946 : 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 31774 : SPI_keepplan(SPIPlanPtr plan)
978 : {
979 : ListCell *lc;
980 :
981 31774 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
982 31774 : 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 31774 : plan->saved = true;
991 31774 : MemoryContextSetParent(plan->plancxt, CacheMemoryContext);
992 :
993 63548 : foreach(lc, plan->plancache_list)
994 : {
995 31774 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
996 :
997 31774 : SaveCachedPlan(plansource);
998 : }
999 :
1000 31774 : 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 7710 : SPI_freeplan(SPIPlanPtr plan)
1027 : {
1028 : ListCell *lc;
1029 :
1030 7710 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1031 0 : return SPI_ERROR_ARGUMENT;
1032 :
1033 : /* Release the plancache entries */
1034 15420 : foreach(lc, plan->plancache_list)
1035 : {
1036 7710 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1037 :
1038 7710 : DropCachedPlan(plansource);
1039 : }
1040 :
1041 : /* Now get rid of the _SPI_plan and subsidiary data in its plancxt */
1042 7710 : MemoryContextDelete(plan->plancxt);
1043 :
1044 7710 : return 0;
1045 : }
1046 :
1047 : HeapTuple
1048 2184 : SPI_copytuple(HeapTuple tuple)
1049 : {
1050 : MemoryContext oldcxt;
1051 : HeapTuple ctuple;
1052 :
1053 2184 : if (tuple == NULL)
1054 : {
1055 0 : SPI_result = SPI_ERROR_ARGUMENT;
1056 0 : return NULL;
1057 : }
1058 :
1059 2184 : if (_SPI_current == NULL)
1060 : {
1061 0 : SPI_result = SPI_ERROR_UNCONNECTED;
1062 0 : return NULL;
1063 : }
1064 :
1065 2184 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1066 :
1067 2184 : ctuple = heap_copytuple(tuple);
1068 :
1069 2184 : MemoryContextSwitchTo(oldcxt);
1070 :
1071 2184 : return ctuple;
1072 : }
1073 :
1074 : HeapTupleHeader
1075 6116 : SPI_returntuple(HeapTuple tuple, TupleDesc tupdesc)
1076 : {
1077 : MemoryContext oldcxt;
1078 : HeapTupleHeader dtup;
1079 :
1080 6116 : if (tuple == NULL || tupdesc == NULL)
1081 : {
1082 0 : SPI_result = SPI_ERROR_ARGUMENT;
1083 0 : return NULL;
1084 : }
1085 :
1086 6116 : 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 6116 : if (tupdesc->tdtypeid == RECORDOID &&
1094 6100 : tupdesc->tdtypmod < 0)
1095 0 : assign_record_type_typmod(tupdesc);
1096 :
1097 6116 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1098 :
1099 6116 : dtup = DatumGetHeapTupleHeader(heap_copy_tuple_as_datum(tuple, tupdesc));
1100 :
1101 6116 : MemoryContextSwitchTo(oldcxt);
1102 :
1103 6116 : return dtup;
1104 : }
1105 :
1106 : HeapTuple
1107 12 : 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 12 : 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 12 : if (_SPI_current == NULL)
1124 : {
1125 0 : SPI_result = SPI_ERROR_UNCONNECTED;
1126 0 : return NULL;
1127 : }
1128 :
1129 12 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1130 :
1131 12 : SPI_result = 0;
1132 :
1133 12 : numberOfAttributes = rel->rd_att->natts;
1134 12 : v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
1135 12 : n = (bool *) palloc(numberOfAttributes * sizeof(bool));
1136 :
1137 : /* fetch old values and nulls */
1138 12 : heap_deform_tuple(tuple, rel->rd_att, v, n);
1139 :
1140 : /* replace values and nulls */
1141 24 : for (i = 0; i < natts; i++)
1142 : {
1143 12 : if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
1144 : break;
1145 12 : v[attnum[i] - 1] = Values[i];
1146 12 : n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n');
1147 : }
1148 :
1149 12 : if (i == natts) /* no errors in *attnum */
1150 : {
1151 12 : 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 12 : mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
1158 12 : mtuple->t_self = tuple->t_self;
1159 12 : mtuple->t_tableOid = tuple->t_tableOid;
1160 : }
1161 : else
1162 : {
1163 0 : mtuple = NULL;
1164 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1165 : }
1166 :
1167 12 : pfree(v);
1168 12 : pfree(n);
1169 :
1170 12 : MemoryContextSwitchTo(oldcxt);
1171 :
1172 12 : return mtuple;
1173 : }
1174 :
1175 : int
1176 22012 : SPI_fnumber(TupleDesc tupdesc, const char *fname)
1177 : {
1178 : int res;
1179 : const FormData_pg_attribute *sysatt;
1180 :
1181 117636 : for (res = 0; res < tupdesc->natts; res++)
1182 : {
1183 117622 : Form_pg_attribute attr = TupleDescAttr(tupdesc, res);
1184 :
1185 117622 : if (namestrcmp(&attr->attname, fname) == 0 &&
1186 21998 : !attr->attisdropped)
1187 21998 : return res + 1;
1188 : }
1189 :
1190 14 : sysatt = SystemAttributeByName(fname);
1191 14 : if (sysatt != NULL)
1192 0 : return sysatt->attnum;
1193 :
1194 : /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
1195 14 : return SPI_ERROR_NOATTRIBUTE;
1196 : }
1197 :
1198 : char *
1199 972 : SPI_fname(TupleDesc tupdesc, int fnumber)
1200 : {
1201 : const FormData_pg_attribute *att;
1202 :
1203 972 : SPI_result = 0;
1204 :
1205 972 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1206 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1207 : {
1208 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1209 0 : return NULL;
1210 : }
1211 :
1212 972 : if (fnumber > 0)
1213 972 : att = TupleDescAttr(tupdesc, fnumber - 1);
1214 : else
1215 0 : att = SystemAttributeDefinition(fnumber);
1216 :
1217 972 : return pstrdup(NameStr(att->attname));
1218 : }
1219 :
1220 : char *
1221 9492 : 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 9492 : SPI_result = 0;
1230 :
1231 9492 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1232 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1233 : {
1234 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1235 0 : return NULL;
1236 : }
1237 :
1238 9492 : val = heap_getattr(tuple, fnumber, tupdesc, &isnull);
1239 9492 : if (isnull)
1240 132 : return NULL;
1241 :
1242 9360 : if (fnumber > 0)
1243 9360 : typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1244 : else
1245 0 : typoid = (SystemAttributeDefinition(fnumber))->atttypid;
1246 :
1247 9360 : getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
1248 :
1249 9360 : return OidOutputFunctionCall(foutoid, val);
1250 : }
1251 :
1252 : Datum
1253 61350 : SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
1254 : {
1255 61350 : SPI_result = 0;
1256 :
1257 61350 : 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) NULL;
1263 : }
1264 :
1265 61350 : return heap_getattr(tuple, fnumber, tupdesc, isnull);
1266 : }
1267 :
1268 : char *
1269 0 : SPI_gettype(TupleDesc tupdesc, int fnumber)
1270 : {
1271 : Oid typoid;
1272 : HeapTuple typeTuple;
1273 : char *result;
1274 :
1275 0 : SPI_result = 0;
1276 :
1277 0 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1278 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1279 : {
1280 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1281 0 : return NULL;
1282 : }
1283 :
1284 0 : if (fnumber > 0)
1285 0 : typoid = TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1286 : else
1287 0 : typoid = (SystemAttributeDefinition(fnumber))->atttypid;
1288 :
1289 0 : typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
1290 :
1291 0 : if (!HeapTupleIsValid(typeTuple))
1292 : {
1293 0 : SPI_result = SPI_ERROR_TYPUNKNOWN;
1294 0 : return NULL;
1295 : }
1296 :
1297 0 : result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
1298 0 : ReleaseSysCache(typeTuple);
1299 0 : 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 1362 : SPI_gettypeid(TupleDesc tupdesc, int fnumber)
1310 : {
1311 1362 : SPI_result = 0;
1312 :
1313 1362 : if (fnumber > tupdesc->natts || fnumber == 0 ||
1314 : fnumber <= FirstLowInvalidHeapAttributeNumber)
1315 : {
1316 0 : SPI_result = SPI_ERROR_NOATTRIBUTE;
1317 0 : return InvalidOid;
1318 : }
1319 :
1320 1362 : if (fnumber > 0)
1321 1362 : return TupleDescAttr(tupdesc, fnumber - 1)->atttypid;
1322 : else
1323 0 : return (SystemAttributeDefinition(fnumber))->atttypid;
1324 : }
1325 :
1326 : char *
1327 474 : SPI_getrelname(Relation rel)
1328 : {
1329 474 : return pstrdup(RelationGetRelationName(rel));
1330 : }
1331 :
1332 : char *
1333 282 : SPI_getnspname(Relation rel)
1334 : {
1335 282 : return get_namespace_name(RelationGetNamespace(rel));
1336 : }
1337 :
1338 : void *
1339 38 : SPI_palloc(Size size)
1340 : {
1341 38 : if (_SPI_current == NULL)
1342 0 : elog(ERROR, "SPI_palloc called while not connected to SPI");
1343 :
1344 38 : 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 5868 : SPI_datumTransfer(Datum value, bool typByVal, int typLen)
1363 : {
1364 : MemoryContext oldcxt;
1365 : Datum result;
1366 :
1367 5868 : if (_SPI_current == NULL)
1368 0 : elog(ERROR, "SPI_datumTransfer called while not connected to SPI");
1369 :
1370 5868 : oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
1371 :
1372 5868 : result = datumTransfer(value, typByVal, typLen);
1373 :
1374 5868 : MemoryContextSwitchTo(oldcxt);
1375 :
1376 5868 : 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 187608 : SPI_freetuptable(SPITupleTable *tuptable)
1388 : {
1389 187608 : bool found = false;
1390 :
1391 : /* ignore call if NULL pointer */
1392 187608 : if (tuptable == NULL)
1393 108114 : return;
1394 :
1395 : /*
1396 : * Search only the topmost SPI context for a matching tuple table.
1397 : */
1398 79494 : if (_SPI_current != NULL)
1399 : {
1400 : slist_mutable_iter siter;
1401 :
1402 : /* find tuptable in active list, then remove it */
1403 79494 : slist_foreach_modify(siter, &_SPI_current->tuptables)
1404 : {
1405 : SPITupleTable *tt;
1406 :
1407 79494 : tt = slist_container(SPITupleTable, next, siter.cur);
1408 79494 : if (tt == tuptable)
1409 : {
1410 79494 : slist_delete_current(&siter);
1411 79494 : found = true;
1412 79494 : 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 79494 : 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 79494 : if (tuptable == _SPI_current->tuptable)
1431 0 : _SPI_current->tuptable = NULL;
1432 79494 : if (tuptable == SPI_tuptable)
1433 71900 : SPI_tuptable = NULL;
1434 :
1435 : /* release all memory belonging to tuptable */
1436 79494 : 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 214 : SPI_cursor_open(const char *name, SPIPlanPtr plan,
1447 : 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 214 : paramLI = _SPI_convert_params(plan->nargs, plan->argtypes,
1455 : Values, Nulls);
1456 :
1457 214 : portal = SPI_cursor_open_internal(name, plan, paramLI, read_only);
1458 :
1459 : /* done with the transient ParamListInfo */
1460 214 : if (paramLI)
1461 8 : pfree(paramLI);
1462 :
1463 214 : 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 2618 : SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
1527 : ParamListInfo params, bool read_only)
1528 : {
1529 2618 : 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 9386 : 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 9386 : if (src == NULL || options == NULL)
1542 0 : elog(ERROR, "SPI_cursor_parse_open called with invalid arguments");
1543 :
1544 9386 : SPI_result = _SPI_begin_call(true);
1545 9386 : if (SPI_result < 0)
1546 0 : elog(ERROR, "SPI_cursor_parse_open called while not connected");
1547 :
1548 9386 : memset(&plan, 0, sizeof(_SPI_plan));
1549 9386 : plan.magic = _SPI_PLAN_MAGIC;
1550 9386 : plan.parse_mode = RAW_PARSE_DEFAULT;
1551 9386 : plan.cursor_options = options->cursorOptions;
1552 9386 : if (options->params)
1553 : {
1554 12 : plan.parserSetup = options->params->parserSetup;
1555 12 : plan.parserSetupArg = options->params->parserSetupArg;
1556 : }
1557 :
1558 9386 : _SPI_prepare_plan(src, &plan);
1559 :
1560 : /* We needn't copy the plan; SPI_cursor_open_internal will do so */
1561 :
1562 9386 : result = SPI_cursor_open_internal(name, &plan,
1563 9386 : options->params, options->read_only);
1564 :
1565 : /* And clean up */
1566 9386 : _SPI_end_call(true);
1567 :
1568 9386 : 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 12218 : 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 12218 : 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 12218 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1618 :
1619 : /* Push the SPI stack */
1620 12218 : 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 12218 : SPI_processed = 0;
1625 12218 : SPI_tuptable = NULL;
1626 12218 : _SPI_current->processed = 0;
1627 12218 : _SPI_current->tuptable = NULL;
1628 :
1629 : /* Create the portal */
1630 12218 : if (name == NULL || name[0] == '\0')
1631 : {
1632 : /* Use a random nonconflicting name */
1633 12170 : portal = CreateNewPortal();
1634 : }
1635 : else
1636 : {
1637 : /* In this path, error if portal of same name already exists */
1638 48 : portal = CreatePortal(name, false, false);
1639 : }
1640 :
1641 : /* Copy the plan's query string into the portal */
1642 12218 : 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 12218 : spicallbackarg.query = plansource->query_string;
1650 12218 : spicallbackarg.mode = plan->parse_mode;
1651 12218 : spierrcontext.callback = _SPI_error_callback;
1652 12218 : spierrcontext.arg = &spicallbackarg;
1653 12218 : spierrcontext.previous = error_context_stack;
1654 12218 : 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 12218 : cplan = GetCachedPlan(plansource, paramLI, NULL, _SPI_current->queryEnv);
1664 12218 : stmt_list = cplan->stmt_list;
1665 :
1666 12218 : 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 9588 : oldcontext = MemoryContextSwitchTo(portal->portalContext);
1675 9588 : stmt_list = copyObject(stmt_list);
1676 9588 : MemoryContextSwitchTo(oldcontext);
1677 9588 : ReleaseCachedPlan(cplan, NULL);
1678 9588 : cplan = NULL; /* portal shouldn't depend on cplan */
1679 : }
1680 :
1681 : /*
1682 : * Set up the portal.
1683 : */
1684 12218 : PortalDefineQuery(portal,
1685 : NULL, /* no statement name */
1686 : query_string,
1687 : plansource->commandTag,
1688 : stmt_list,
1689 : cplan,
1690 : plansource);
1691 :
1692 : /*
1693 : * Set up options for portal. Default SCROLL type is chosen the same way
1694 : * as PerformCursorOpen does it.
1695 : */
1696 12218 : portal->cursorOptions = plan->cursor_options;
1697 12218 : if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
1698 : {
1699 420 : if (list_length(stmt_list) == 1 &&
1700 420 : linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1701 838 : linitial_node(PlannedStmt, stmt_list)->rowMarks == NIL &&
1702 418 : ExecSupportsBackwardScan(linitial_node(PlannedStmt, stmt_list)->planTree))
1703 388 : portal->cursorOptions |= CURSOR_OPT_SCROLL;
1704 : else
1705 32 : portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
1706 : }
1707 :
1708 : /*
1709 : * Disallow SCROLL with SELECT FOR UPDATE. This is not redundant with the
1710 : * check in transformDeclareCursorStmt because the cursor options might
1711 : * not have come through there.
1712 : */
1713 12218 : if (portal->cursorOptions & CURSOR_OPT_SCROLL)
1714 : {
1715 412 : if (list_length(stmt_list) == 1 &&
1716 412 : linitial_node(PlannedStmt, stmt_list)->commandType != CMD_UTILITY &&
1717 412 : linitial_node(PlannedStmt, stmt_list)->rowMarks != NIL)
1718 0 : ereport(ERROR,
1719 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1720 : errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"),
1721 : errdetail("Scrollable cursors must be READ ONLY.")));
1722 : }
1723 :
1724 : /* Make current query environment available to portal at execution time. */
1725 12218 : portal->queryEnv = _SPI_current->queryEnv;
1726 :
1727 : /*
1728 : * If told to be read-only, we'd better check for read-only queries. This
1729 : * can't be done earlier because we need to look at the finished, planned
1730 : * queries. (In particular, we don't want to do it between GetCachedPlan
1731 : * and PortalDefineQuery, because throwing an error between those steps
1732 : * would result in leaking our plancache refcount.)
1733 : */
1734 12218 : if (read_only)
1735 : {
1736 : ListCell *lc;
1737 :
1738 312 : foreach(lc, stmt_list)
1739 : {
1740 156 : PlannedStmt *pstmt = lfirst_node(PlannedStmt, lc);
1741 :
1742 156 : if (!CommandIsReadOnly(pstmt))
1743 0 : ereport(ERROR,
1744 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1745 : /* translator: %s is a SQL statement name */
1746 : errmsg("%s is not allowed in a non-volatile function",
1747 : CreateCommandName((Node *) pstmt))));
1748 : }
1749 : }
1750 :
1751 : /* Set up the snapshot to use. */
1752 12218 : if (read_only)
1753 156 : snapshot = GetActiveSnapshot();
1754 : else
1755 : {
1756 12062 : CommandCounterIncrement();
1757 12062 : snapshot = GetTransactionSnapshot();
1758 : }
1759 :
1760 : /*
1761 : * If the plan has parameters, copy them into the portal. Note that this
1762 : * must be done after revalidating the plan, because in dynamic parameter
1763 : * cases the set of parameters could have changed during re-parsing.
1764 : */
1765 12218 : if (paramLI)
1766 : {
1767 680 : oldcontext = MemoryContextSwitchTo(portal->portalContext);
1768 680 : paramLI = copyParamList(paramLI);
1769 680 : MemoryContextSwitchTo(oldcontext);
1770 : }
1771 :
1772 : /*
1773 : * Start portal execution.
1774 : */
1775 12218 : PortalStart(portal, paramLI, 0, snapshot);
1776 :
1777 : Assert(portal->strategy != PORTAL_MULTI_QUERY);
1778 :
1779 : /* Pop the error context stack */
1780 12218 : error_context_stack = spierrcontext.previous;
1781 :
1782 : /* Pop the SPI stack */
1783 12218 : _SPI_end_call(true);
1784 :
1785 : /* Return the created portal */
1786 12218 : return portal;
1787 : }
1788 :
1789 :
1790 : /*
1791 : * SPI_cursor_find()
1792 : *
1793 : * Find the portal of an existing open cursor
1794 : */
1795 : Portal
1796 560 : SPI_cursor_find(const char *name)
1797 : {
1798 560 : return GetPortalByName(name);
1799 : }
1800 :
1801 :
1802 : /*
1803 : * SPI_cursor_fetch()
1804 : *
1805 : * Fetch rows in a cursor
1806 : */
1807 : void
1808 43848 : SPI_cursor_fetch(Portal portal, bool forward, long count)
1809 : {
1810 43848 : _SPI_cursor_operation(portal,
1811 43848 : forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1812 : CreateDestReceiver(DestSPI));
1813 : /* we know that the DestSPI receiver doesn't need a destroy call */
1814 43840 : }
1815 :
1816 :
1817 : /*
1818 : * SPI_cursor_move()
1819 : *
1820 : * Move in a cursor
1821 : */
1822 : void
1823 0 : SPI_cursor_move(Portal portal, bool forward, long count)
1824 : {
1825 0 : _SPI_cursor_operation(portal,
1826 0 : forward ? FETCH_FORWARD : FETCH_BACKWARD, count,
1827 : None_Receiver);
1828 0 : }
1829 :
1830 :
1831 : /*
1832 : * SPI_scroll_cursor_fetch()
1833 : *
1834 : * Fetch rows in a scrollable cursor
1835 : */
1836 : void
1837 300 : SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction, long count)
1838 : {
1839 300 : _SPI_cursor_operation(portal,
1840 : direction, count,
1841 : CreateDestReceiver(DestSPI));
1842 : /* we know that the DestSPI receiver doesn't need a destroy call */
1843 294 : }
1844 :
1845 :
1846 : /*
1847 : * SPI_scroll_cursor_move()
1848 : *
1849 : * Move in a scrollable cursor
1850 : */
1851 : void
1852 42 : SPI_scroll_cursor_move(Portal portal, FetchDirection direction, long count)
1853 : {
1854 42 : _SPI_cursor_operation(portal, direction, count, None_Receiver);
1855 42 : }
1856 :
1857 :
1858 : /*
1859 : * SPI_cursor_close()
1860 : *
1861 : * Close a cursor
1862 : */
1863 : void
1864 12110 : SPI_cursor_close(Portal portal)
1865 : {
1866 12110 : if (!PortalIsValid(portal))
1867 0 : elog(ERROR, "invalid portal in SPI cursor operation");
1868 :
1869 12110 : PortalDrop(portal, false);
1870 12110 : }
1871 :
1872 : /*
1873 : * Returns the Oid representing the type id for argument at argIndex. First
1874 : * parameter is at index zero.
1875 : */
1876 : Oid
1877 0 : SPI_getargtypeid(SPIPlanPtr plan, int argIndex)
1878 : {
1879 0 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC ||
1880 0 : argIndex < 0 || argIndex >= plan->nargs)
1881 : {
1882 0 : SPI_result = SPI_ERROR_ARGUMENT;
1883 0 : return InvalidOid;
1884 : }
1885 0 : return plan->argtypes[argIndex];
1886 : }
1887 :
1888 : /*
1889 : * Returns the number of arguments for the prepared plan.
1890 : */
1891 : int
1892 0 : SPI_getargcount(SPIPlanPtr plan)
1893 : {
1894 0 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1895 : {
1896 0 : SPI_result = SPI_ERROR_ARGUMENT;
1897 0 : return -1;
1898 : }
1899 0 : return plan->nargs;
1900 : }
1901 :
1902 : /*
1903 : * Returns true if the plan contains exactly one command
1904 : * and that command returns tuples to the caller (eg, SELECT or
1905 : * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
1906 : * the result indicates if the command can be used with SPI_cursor_open
1907 : *
1908 : * Parameters
1909 : * plan: A plan previously prepared using SPI_prepare
1910 : */
1911 : bool
1912 12218 : SPI_is_cursor_plan(SPIPlanPtr plan)
1913 : {
1914 : CachedPlanSource *plansource;
1915 :
1916 12218 : if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC)
1917 : {
1918 0 : SPI_result = SPI_ERROR_ARGUMENT;
1919 0 : return false;
1920 : }
1921 :
1922 12218 : if (list_length(plan->plancache_list) != 1)
1923 : {
1924 0 : SPI_result = 0;
1925 0 : return false; /* not exactly 1 pre-rewrite command */
1926 : }
1927 12218 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
1928 :
1929 : /*
1930 : * We used to force revalidation of the cached plan here, but that seems
1931 : * unnecessary: invalidation could mean a change in the rowtype of the
1932 : * tuples returned by a plan, but not whether it returns tuples at all.
1933 : */
1934 12218 : SPI_result = 0;
1935 :
1936 : /* Does it return tuples? */
1937 12218 : if (plansource->resultDesc)
1938 12218 : return true;
1939 :
1940 0 : return false;
1941 : }
1942 :
1943 : /*
1944 : * SPI_plan_is_valid --- test whether a SPI plan is currently valid
1945 : * (that is, not marked as being in need of revalidation).
1946 : *
1947 : * See notes for CachedPlanIsValid before using this.
1948 : */
1949 : bool
1950 3748 : SPI_plan_is_valid(SPIPlanPtr plan)
1951 : {
1952 : ListCell *lc;
1953 :
1954 : Assert(plan->magic == _SPI_PLAN_MAGIC);
1955 :
1956 7098 : foreach(lc, plan->plancache_list)
1957 : {
1958 3748 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1959 :
1960 3748 : if (!CachedPlanIsValid(plansource))
1961 398 : return false;
1962 : }
1963 3350 : return true;
1964 : }
1965 :
1966 : /*
1967 : * SPI_result_code_string --- convert any SPI return code to a string
1968 : *
1969 : * This is often useful in error messages. Most callers will probably
1970 : * only pass negative (error-case) codes, but for generality we recognize
1971 : * the success codes too.
1972 : */
1973 : const char *
1974 120 : SPI_result_code_string(int code)
1975 : {
1976 : static char buf[64];
1977 :
1978 120 : switch (code)
1979 : {
1980 0 : case SPI_ERROR_CONNECT:
1981 0 : return "SPI_ERROR_CONNECT";
1982 0 : case SPI_ERROR_COPY:
1983 0 : return "SPI_ERROR_COPY";
1984 0 : case SPI_ERROR_OPUNKNOWN:
1985 0 : return "SPI_ERROR_OPUNKNOWN";
1986 0 : case SPI_ERROR_UNCONNECTED:
1987 0 : return "SPI_ERROR_UNCONNECTED";
1988 0 : case SPI_ERROR_ARGUMENT:
1989 0 : return "SPI_ERROR_ARGUMENT";
1990 0 : case SPI_ERROR_PARAM:
1991 0 : return "SPI_ERROR_PARAM";
1992 6 : case SPI_ERROR_TRANSACTION:
1993 6 : return "SPI_ERROR_TRANSACTION";
1994 0 : case SPI_ERROR_NOATTRIBUTE:
1995 0 : return "SPI_ERROR_NOATTRIBUTE";
1996 0 : case SPI_ERROR_NOOUTFUNC:
1997 0 : return "SPI_ERROR_NOOUTFUNC";
1998 0 : case SPI_ERROR_TYPUNKNOWN:
1999 0 : return "SPI_ERROR_TYPUNKNOWN";
2000 0 : case SPI_ERROR_REL_DUPLICATE:
2001 0 : return "SPI_ERROR_REL_DUPLICATE";
2002 0 : case SPI_ERROR_REL_NOT_FOUND:
2003 0 : return "SPI_ERROR_REL_NOT_FOUND";
2004 0 : case SPI_OK_CONNECT:
2005 0 : return "SPI_OK_CONNECT";
2006 0 : case SPI_OK_FINISH:
2007 0 : return "SPI_OK_FINISH";
2008 0 : case SPI_OK_FETCH:
2009 0 : return "SPI_OK_FETCH";
2010 2 : case SPI_OK_UTILITY:
2011 2 : return "SPI_OK_UTILITY";
2012 22 : case SPI_OK_SELECT:
2013 22 : return "SPI_OK_SELECT";
2014 0 : case SPI_OK_SELINTO:
2015 0 : return "SPI_OK_SELINTO";
2016 90 : case SPI_OK_INSERT:
2017 90 : return "SPI_OK_INSERT";
2018 0 : case SPI_OK_DELETE:
2019 0 : return "SPI_OK_DELETE";
2020 0 : case SPI_OK_UPDATE:
2021 0 : return "SPI_OK_UPDATE";
2022 0 : case SPI_OK_CURSOR:
2023 0 : return "SPI_OK_CURSOR";
2024 0 : case SPI_OK_INSERT_RETURNING:
2025 0 : return "SPI_OK_INSERT_RETURNING";
2026 0 : case SPI_OK_DELETE_RETURNING:
2027 0 : return "SPI_OK_DELETE_RETURNING";
2028 0 : case SPI_OK_UPDATE_RETURNING:
2029 0 : return "SPI_OK_UPDATE_RETURNING";
2030 0 : case SPI_OK_REWRITTEN:
2031 0 : return "SPI_OK_REWRITTEN";
2032 0 : case SPI_OK_REL_REGISTER:
2033 0 : return "SPI_OK_REL_REGISTER";
2034 0 : case SPI_OK_REL_UNREGISTER:
2035 0 : return "SPI_OK_REL_UNREGISTER";
2036 0 : case SPI_OK_TD_REGISTER:
2037 0 : return "SPI_OK_TD_REGISTER";
2038 0 : case SPI_OK_MERGE:
2039 0 : return "SPI_OK_MERGE";
2040 0 : case SPI_OK_MERGE_RETURNING:
2041 0 : return "SPI_OK_MERGE_RETURNING";
2042 : }
2043 : /* Unrecognized code ... return something useful ... */
2044 0 : sprintf(buf, "Unrecognized SPI code %d", code);
2045 0 : return buf;
2046 : }
2047 :
2048 : /*
2049 : * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of
2050 : * CachedPlanSources.
2051 : *
2052 : * CAUTION: there is no check on whether the CachedPlanSources are up-to-date.
2053 : *
2054 : * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
2055 : * look directly into the SPIPlan for itself). It's not documented in
2056 : * spi.sgml because we'd just as soon not have too many places using this.
2057 : */
2058 : List *
2059 60326 : SPI_plan_get_plan_sources(SPIPlanPtr plan)
2060 : {
2061 : Assert(plan->magic == _SPI_PLAN_MAGIC);
2062 60326 : return plan->plancache_list;
2063 : }
2064 :
2065 : /*
2066 : * SPI_plan_get_cached_plan --- get a SPI plan's generic CachedPlan,
2067 : * if the SPI plan contains exactly one CachedPlanSource. If not,
2068 : * return NULL.
2069 : *
2070 : * The plan's refcount is incremented (and logged in CurrentResourceOwner,
2071 : * if it's a saved plan). Caller is responsible for doing ReleaseCachedPlan.
2072 : *
2073 : * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL
2074 : * look directly into the SPIPlan for itself). It's not documented in
2075 : * spi.sgml because we'd just as soon not have too many places using this.
2076 : */
2077 : CachedPlan *
2078 29756 : SPI_plan_get_cached_plan(SPIPlanPtr plan)
2079 : {
2080 : CachedPlanSource *plansource;
2081 : CachedPlan *cplan;
2082 : SPICallbackArg spicallbackarg;
2083 : ErrorContextCallback spierrcontext;
2084 :
2085 : Assert(plan->magic == _SPI_PLAN_MAGIC);
2086 :
2087 : /* Can't support one-shot plans here */
2088 29756 : if (plan->oneshot)
2089 0 : return NULL;
2090 :
2091 : /* Must have exactly one CachedPlanSource */
2092 29756 : if (list_length(plan->plancache_list) != 1)
2093 0 : return NULL;
2094 29756 : plansource = (CachedPlanSource *) linitial(plan->plancache_list);
2095 :
2096 : /* Setup error traceback support for ereport() */
2097 29756 : spicallbackarg.query = plansource->query_string;
2098 29756 : spicallbackarg.mode = plan->parse_mode;
2099 29756 : spierrcontext.callback = _SPI_error_callback;
2100 29756 : spierrcontext.arg = &spicallbackarg;
2101 29756 : spierrcontext.previous = error_context_stack;
2102 29756 : error_context_stack = &spierrcontext;
2103 :
2104 : /* Get the generic plan for the query */
2105 29756 : cplan = GetCachedPlan(plansource, NULL,
2106 29756 : plan->saved ? CurrentResourceOwner : NULL,
2107 29756 : _SPI_current->queryEnv);
2108 : Assert(cplan == plansource->gplan);
2109 :
2110 : /* Pop the error context stack */
2111 29718 : error_context_stack = spierrcontext.previous;
2112 :
2113 29718 : return cplan;
2114 : }
2115 :
2116 :
2117 : /* =================== private functions =================== */
2118 :
2119 : /*
2120 : * spi_dest_startup
2121 : * Initialize to receive tuples from Executor into SPITupleTable
2122 : * of current SPI procedure
2123 : */
2124 : void
2125 97046 : spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
2126 : {
2127 : SPITupleTable *tuptable;
2128 : MemoryContext oldcxt;
2129 : MemoryContext tuptabcxt;
2130 :
2131 97046 : if (_SPI_current == NULL)
2132 0 : elog(ERROR, "spi_dest_startup called while not connected to SPI");
2133 :
2134 97046 : if (_SPI_current->tuptable != NULL)
2135 0 : elog(ERROR, "improper call to spi_dest_startup");
2136 :
2137 : /* We create the tuple table context as a child of procCxt */
2138 :
2139 97046 : oldcxt = _SPI_procmem(); /* switch to procedure memory context */
2140 :
2141 97046 : tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
2142 : "SPI TupTable",
2143 : ALLOCSET_DEFAULT_SIZES);
2144 97046 : MemoryContextSwitchTo(tuptabcxt);
2145 :
2146 97046 : _SPI_current->tuptable = tuptable = (SPITupleTable *)
2147 97046 : palloc0(sizeof(SPITupleTable));
2148 97046 : tuptable->tuptabcxt = tuptabcxt;
2149 97046 : tuptable->subid = GetCurrentSubTransactionId();
2150 :
2151 : /*
2152 : * The tuptable is now valid enough to be freed by AtEOSubXact_SPI, so put
2153 : * it onto the SPI context's tuptables list. This will ensure it's not
2154 : * leaked even in the unlikely event the following few lines fail.
2155 : */
2156 97046 : slist_push_head(&_SPI_current->tuptables, &tuptable->next);
2157 :
2158 : /* set up initial allocations */
2159 97046 : tuptable->alloced = 128;
2160 97046 : tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
2161 97046 : tuptable->numvals = 0;
2162 97046 : tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
2163 :
2164 97046 : MemoryContextSwitchTo(oldcxt);
2165 97046 : }
2166 :
2167 : /*
2168 : * spi_printtup
2169 : * store tuple retrieved by Executor into SPITupleTable
2170 : * of current SPI procedure
2171 : */
2172 : bool
2173 119462 : spi_printtup(TupleTableSlot *slot, DestReceiver *self)
2174 : {
2175 : SPITupleTable *tuptable;
2176 : MemoryContext oldcxt;
2177 :
2178 119462 : if (_SPI_current == NULL)
2179 0 : elog(ERROR, "spi_printtup called while not connected to SPI");
2180 :
2181 119462 : tuptable = _SPI_current->tuptable;
2182 119462 : if (tuptable == NULL)
2183 0 : elog(ERROR, "improper call to spi_printtup");
2184 :
2185 119462 : oldcxt = MemoryContextSwitchTo(tuptable->tuptabcxt);
2186 :
2187 119462 : if (tuptable->numvals >= tuptable->alloced)
2188 : {
2189 : /* Double the size of the pointer array */
2190 0 : uint64 newalloced = tuptable->alloced * 2;
2191 :
2192 0 : tuptable->vals = (HeapTuple *) repalloc_huge(tuptable->vals,
2193 : newalloced * sizeof(HeapTuple));
2194 0 : tuptable->alloced = newalloced;
2195 : }
2196 :
2197 119462 : tuptable->vals[tuptable->numvals] = ExecCopySlotHeapTuple(slot);
2198 119462 : (tuptable->numvals)++;
2199 :
2200 119462 : MemoryContextSwitchTo(oldcxt);
2201 :
2202 119462 : return true;
2203 : }
2204 :
2205 : /*
2206 : * Static functions
2207 : */
2208 :
2209 : /*
2210 : * Parse and analyze a querystring.
2211 : *
2212 : * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
2213 : * and plan->parserSetupArg) must be valid, as must plan->parse_mode and
2214 : * plan->cursor_options.
2215 : *
2216 : * Results are stored into *plan (specifically, plan->plancache_list).
2217 : * Note that the result data is all in CurrentMemoryContext or child contexts
2218 : * thereof; in practice this means it is in the SPI executor context, and
2219 : * what we are creating is a "temporary" SPIPlan. Cruft generated during
2220 : * parsing is also left in CurrentMemoryContext.
2221 : */
2222 : static void
2223 42684 : _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
2224 : {
2225 : List *raw_parsetree_list;
2226 : List *plancache_list;
2227 : ListCell *list_item;
2228 : SPICallbackArg spicallbackarg;
2229 : ErrorContextCallback spierrcontext;
2230 :
2231 : /*
2232 : * Setup error traceback support for ereport()
2233 : */
2234 42684 : spicallbackarg.query = src;
2235 42684 : spicallbackarg.mode = plan->parse_mode;
2236 42684 : spierrcontext.callback = _SPI_error_callback;
2237 42684 : spierrcontext.arg = &spicallbackarg;
2238 42684 : spierrcontext.previous = error_context_stack;
2239 42684 : error_context_stack = &spierrcontext;
2240 :
2241 : /*
2242 : * Parse the request string into a list of raw parse trees.
2243 : */
2244 42684 : raw_parsetree_list = raw_parser(src, plan->parse_mode);
2245 :
2246 : /*
2247 : * Do parse analysis and rule rewrite for each raw parsetree, storing the
2248 : * results into unsaved plancache entries.
2249 : */
2250 42684 : plancache_list = NIL;
2251 :
2252 85272 : foreach(list_item, raw_parsetree_list)
2253 : {
2254 42684 : RawStmt *parsetree = lfirst_node(RawStmt, list_item);
2255 : List *stmt_list;
2256 : CachedPlanSource *plansource;
2257 :
2258 : /*
2259 : * Create the CachedPlanSource before we do parse analysis, since it
2260 : * needs to see the unmodified raw parse tree.
2261 : */
2262 42684 : plansource = CreateCachedPlan(parsetree,
2263 : src,
2264 : CreateCommandTag(parsetree->stmt));
2265 :
2266 : /*
2267 : * Parameter datatypes are driven by parserSetup hook if provided,
2268 : * otherwise we use the fixed parameter list.
2269 : */
2270 42684 : if (plan->parserSetup != NULL)
2271 : {
2272 : Assert(plan->nargs == 0);
2273 28052 : stmt_list = pg_analyze_and_rewrite_withcb(parsetree,
2274 : src,
2275 : plan->parserSetup,
2276 : plan->parserSetupArg,
2277 28052 : _SPI_current->queryEnv);
2278 : }
2279 : else
2280 : {
2281 14632 : stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
2282 : src,
2283 14632 : plan->argtypes,
2284 : plan->nargs,
2285 14632 : _SPI_current->queryEnv);
2286 : }
2287 :
2288 : /* Finish filling in the CachedPlanSource */
2289 42588 : CompleteCachedPlan(plansource,
2290 : stmt_list,
2291 : NULL,
2292 : plan->argtypes,
2293 : plan->nargs,
2294 : plan->parserSetup,
2295 : plan->parserSetupArg,
2296 : plan->cursor_options,
2297 : false); /* not fixed result */
2298 :
2299 42588 : plancache_list = lappend(plancache_list, plansource);
2300 : }
2301 :
2302 42588 : plan->plancache_list = plancache_list;
2303 42588 : plan->oneshot = false;
2304 :
2305 : /*
2306 : * Pop the error context stack
2307 : */
2308 42588 : error_context_stack = spierrcontext.previous;
2309 42588 : }
2310 :
2311 : /*
2312 : * Parse, but don't analyze, a querystring.
2313 : *
2314 : * This is a stripped-down version of _SPI_prepare_plan that only does the
2315 : * initial raw parsing. It creates "one shot" CachedPlanSources
2316 : * that still require parse analysis before execution is possible.
2317 : *
2318 : * The advantage of using the "one shot" form of CachedPlanSource is that
2319 : * we eliminate data copying and invalidation overhead. Postponing parse
2320 : * analysis also prevents issues if some of the raw parsetrees are DDL
2321 : * commands that affect validity of later parsetrees. Both of these
2322 : * attributes are good things for SPI_execute() and similar cases.
2323 : *
2324 : * Results are stored into *plan (specifically, plan->plancache_list).
2325 : * Note that the result data is all in CurrentMemoryContext or child contexts
2326 : * thereof; in practice this means it is in the SPI executor context, and
2327 : * what we are creating is a "temporary" SPIPlan. Cruft generated during
2328 : * parsing is also left in CurrentMemoryContext.
2329 : */
2330 : static void
2331 16322 : _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
2332 : {
2333 : List *raw_parsetree_list;
2334 : List *plancache_list;
2335 : ListCell *list_item;
2336 : SPICallbackArg spicallbackarg;
2337 : ErrorContextCallback spierrcontext;
2338 :
2339 : /*
2340 : * Setup error traceback support for ereport()
2341 : */
2342 16322 : spicallbackarg.query = src;
2343 16322 : spicallbackarg.mode = plan->parse_mode;
2344 16322 : spierrcontext.callback = _SPI_error_callback;
2345 16322 : spierrcontext.arg = &spicallbackarg;
2346 16322 : spierrcontext.previous = error_context_stack;
2347 16322 : error_context_stack = &spierrcontext;
2348 :
2349 : /*
2350 : * Parse the request string into a list of raw parse trees.
2351 : */
2352 16322 : raw_parsetree_list = raw_parser(src, plan->parse_mode);
2353 :
2354 : /*
2355 : * Construct plancache entries, but don't do parse analysis yet.
2356 : */
2357 16306 : plancache_list = NIL;
2358 :
2359 32614 : foreach(list_item, raw_parsetree_list)
2360 : {
2361 16308 : RawStmt *parsetree = lfirst_node(RawStmt, list_item);
2362 : CachedPlanSource *plansource;
2363 :
2364 16308 : plansource = CreateOneShotCachedPlan(parsetree,
2365 : src,
2366 : CreateCommandTag(parsetree->stmt));
2367 :
2368 16308 : plancache_list = lappend(plancache_list, plansource);
2369 : }
2370 :
2371 16306 : plan->plancache_list = plancache_list;
2372 16306 : plan->oneshot = true;
2373 :
2374 : /*
2375 : * Pop the error context stack
2376 : */
2377 16306 : error_context_stack = spierrcontext.previous;
2378 16306 : }
2379 :
2380 : /*
2381 : * _SPI_execute_plan: execute the given plan with the given options
2382 : *
2383 : * options contains options accessible from outside SPI:
2384 : * params: parameter values to pass to query
2385 : * read_only: true for read-only execution (no CommandCounterIncrement)
2386 : * allow_nonatomic: true to allow nonatomic CALL/DO execution
2387 : * must_return_tuples: throw error if query doesn't return tuples
2388 : * tcount: execution tuple-count limit, or 0 for none
2389 : * dest: DestReceiver to receive output, or NULL for normal SPI output
2390 : * owner: ResourceOwner that will be used to hold refcount on plan;
2391 : * if NULL, CurrentResourceOwner is used (ignored for non-saved plan)
2392 : *
2393 : * Additional, only-internally-accessible options:
2394 : * snapshot: query snapshot to use, or InvalidSnapshot for the normal
2395 : * behavior of taking a new snapshot for each query.
2396 : * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
2397 : * fire_triggers: true to fire AFTER triggers at end of query (normal case);
2398 : * false means any AFTER triggers are postponed to end of outer query
2399 : */
2400 : static int
2401 109320 : _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
2402 : Snapshot snapshot, Snapshot crosscheck_snapshot,
2403 : bool fire_triggers)
2404 : {
2405 109320 : int my_res = 0;
2406 109320 : uint64 my_processed = 0;
2407 109320 : SPITupleTable *my_tuptable = NULL;
2408 109320 : int res = 0;
2409 : bool allow_nonatomic;
2410 109320 : bool pushed_active_snap = false;
2411 109320 : ResourceOwner plan_owner = options->owner;
2412 : SPICallbackArg spicallbackarg;
2413 : ErrorContextCallback spierrcontext;
2414 109320 : CachedPlan *cplan = NULL;
2415 : ListCell *lc1;
2416 :
2417 : /*
2418 : * We allow nonatomic behavior only if options->allow_nonatomic is set
2419 : * *and* the SPI_OPT_NONATOMIC flag was given when connecting and we are
2420 : * not inside a subtransaction. The latter two tests match whether
2421 : * _SPI_commit() would allow a commit; see there for more commentary.
2422 : */
2423 218758 : allow_nonatomic = options->allow_nonatomic &&
2424 109320 : !_SPI_current->atomic && !IsSubTransaction();
2425 :
2426 : /*
2427 : * Setup error traceback support for ereport()
2428 : */
2429 109320 : spicallbackarg.query = NULL; /* we'll fill this below */
2430 109320 : spicallbackarg.mode = plan->parse_mode;
2431 109320 : spierrcontext.callback = _SPI_error_callback;
2432 109320 : spierrcontext.arg = &spicallbackarg;
2433 109320 : spierrcontext.previous = error_context_stack;
2434 109320 : error_context_stack = &spierrcontext;
2435 :
2436 : /*
2437 : * We support four distinct snapshot management behaviors:
2438 : *
2439 : * snapshot != InvalidSnapshot, read_only = true: use exactly the given
2440 : * snapshot.
2441 : *
2442 : * snapshot != InvalidSnapshot, read_only = false: use the given snapshot,
2443 : * modified by advancing its command ID before each querytree.
2444 : *
2445 : * snapshot == InvalidSnapshot, read_only = true: do nothing for queries
2446 : * that require no snapshot. For those that do, ensure that a Portal
2447 : * snapshot exists; then use that, or use the entry-time ActiveSnapshot if
2448 : * that exists and is different.
2449 : *
2450 : * snapshot == InvalidSnapshot, read_only = false: do nothing for queries
2451 : * that require no snapshot. For those that do, ensure that a Portal
2452 : * snapshot exists; then, in atomic execution (!allow_nonatomic) take a
2453 : * full new snapshot for each user command, and advance its command ID
2454 : * before each querytree within the command. In allow_nonatomic mode we
2455 : * just use the Portal snapshot unmodified.
2456 : *
2457 : * In the first two cases, we can just push the snap onto the stack once
2458 : * for the whole plan list.
2459 : *
2460 : * Note that snapshot != InvalidSnapshot implies an atomic execution
2461 : * context.
2462 : */
2463 109320 : if (snapshot != InvalidSnapshot)
2464 : {
2465 : /* this intentionally tests the options field not the derived value */
2466 : Assert(!options->allow_nonatomic);
2467 1110 : if (options->read_only)
2468 : {
2469 1078 : PushActiveSnapshot(snapshot);
2470 1078 : pushed_active_snap = true;
2471 : }
2472 : else
2473 : {
2474 : /* Make sure we have a private copy of the snapshot to modify */
2475 32 : PushCopiedSnapshot(snapshot);
2476 32 : pushed_active_snap = true;
2477 : }
2478 : }
2479 :
2480 : /*
2481 : * Ensure that we have a resource owner if plan is saved, and not if it
2482 : * isn't.
2483 : */
2484 109320 : if (!plan->saved)
2485 17532 : plan_owner = NULL;
2486 91788 : else if (plan_owner == NULL)
2487 91678 : plan_owner = CurrentResourceOwner;
2488 :
2489 : /*
2490 : * We interpret must_return_tuples as "there must be at least one query,
2491 : * and all of them must return tuples". This is a bit laxer than
2492 : * SPI_is_cursor_plan's check, but there seems no reason to enforce that
2493 : * there be only one query.
2494 : */
2495 109320 : if (options->must_return_tuples && plan->plancache_list == NIL)
2496 0 : ereport(ERROR,
2497 : (errcode(ERRCODE_SYNTAX_ERROR),
2498 : errmsg("empty query does not return tuples")));
2499 :
2500 212956 : foreach(lc1, plan->plancache_list)
2501 : {
2502 109320 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
2503 : List *stmt_list;
2504 : ListCell *lc2;
2505 109320 : int query_index = 0;
2506 :
2507 109320 : spicallbackarg.query = plansource->query_string;
2508 :
2509 : /*
2510 : * If this is a one-shot plan, we still need to do parse analysis.
2511 : */
2512 109320 : if (plan->oneshot)
2513 : {
2514 16306 : RawStmt *parsetree = plansource->raw_parse_tree;
2515 16306 : const char *src = plansource->query_string;
2516 : List *querytree_list;
2517 :
2518 : /*
2519 : * Parameter datatypes are driven by parserSetup hook if provided,
2520 : * otherwise we use the fixed parameter list.
2521 : */
2522 16306 : if (parsetree == NULL)
2523 0 : querytree_list = NIL;
2524 16306 : else if (plan->parserSetup != NULL)
2525 : {
2526 : Assert(plan->nargs == 0);
2527 570 : querytree_list = pg_analyze_and_rewrite_withcb(parsetree,
2528 : src,
2529 : plan->parserSetup,
2530 : plan->parserSetupArg,
2531 570 : _SPI_current->queryEnv);
2532 : }
2533 : else
2534 : {
2535 15736 : querytree_list = pg_analyze_and_rewrite_fixedparams(parsetree,
2536 : src,
2537 15736 : plan->argtypes,
2538 : plan->nargs,
2539 15736 : _SPI_current->queryEnv);
2540 : }
2541 :
2542 : /* Finish filling in the CachedPlanSource */
2543 16292 : CompleteCachedPlan(plansource,
2544 : querytree_list,
2545 : NULL,
2546 : plan->argtypes,
2547 : plan->nargs,
2548 : plan->parserSetup,
2549 : plan->parserSetupArg,
2550 : plan->cursor_options,
2551 : false); /* not fixed result */
2552 : }
2553 :
2554 : /*
2555 : * If asked to, complain when query does not return tuples.
2556 : * (Replanning can't change this, so we can check it before that.
2557 : * However, we can't check it till after parse analysis, so in the
2558 : * case of a one-shot plan this is the earliest we could check.)
2559 : */
2560 109306 : if (options->must_return_tuples && !plansource->resultDesc)
2561 : {
2562 : /* try to give a good error message */
2563 : const char *cmdtag;
2564 :
2565 : /* A SELECT without resultDesc must be SELECT INTO */
2566 12 : if (plansource->commandTag == CMDTAG_SELECT)
2567 12 : cmdtag = "SELECT INTO";
2568 : else
2569 0 : cmdtag = GetCommandTagName(plansource->commandTag);
2570 12 : ereport(ERROR,
2571 : (errcode(ERRCODE_SYNTAX_ERROR),
2572 : /* translator: %s is name of a SQL command, eg INSERT */
2573 : errmsg("%s query does not return tuples", cmdtag)));
2574 : }
2575 :
2576 : /*
2577 : * Replan if needed, and increment plan refcount. If it's a saved
2578 : * plan, the refcount must be backed by the plan_owner.
2579 : */
2580 109294 : cplan = GetCachedPlan(plansource, options->params,
2581 109294 : plan_owner, _SPI_current->queryEnv);
2582 :
2583 109152 : stmt_list = cplan->stmt_list;
2584 :
2585 : /*
2586 : * If we weren't given a specific snapshot to use, and the statement
2587 : * list requires a snapshot, set that up.
2588 : */
2589 217194 : if (snapshot == InvalidSnapshot &&
2590 216084 : (list_length(stmt_list) > 1 ||
2591 216084 : (list_length(stmt_list) == 1 &&
2592 108042 : PlannedStmtRequiresSnapshot(linitial_node(PlannedStmt,
2593 : stmt_list)))))
2594 : {
2595 : /*
2596 : * First, ensure there's a Portal-level snapshot. This back-fills
2597 : * the snapshot stack in case the previous operation was a COMMIT
2598 : * or ROLLBACK inside a procedure or DO block. (We can't put back
2599 : * the Portal snapshot any sooner, or we'd break cases like doing
2600 : * SET or LOCK just after COMMIT.) It's enough to check once per
2601 : * statement list, since COMMIT/ROLLBACK/CALL/DO can't appear
2602 : * within a multi-statement list.
2603 : */
2604 97284 : EnsurePortalSnapshotExists();
2605 :
2606 : /*
2607 : * In the default non-read-only case, get a new per-statement-list
2608 : * snapshot, replacing any that we pushed in a previous cycle.
2609 : * Skip it when doing non-atomic execution, though (we rely
2610 : * entirely on the Portal snapshot in that case).
2611 : */
2612 97284 : if (!options->read_only && !allow_nonatomic)
2613 : {
2614 92296 : if (pushed_active_snap)
2615 0 : PopActiveSnapshot();
2616 92296 : PushActiveSnapshot(GetTransactionSnapshot());
2617 92296 : pushed_active_snap = true;
2618 : }
2619 : }
2620 :
2621 212788 : foreach(lc2, stmt_list)
2622 : {
2623 109152 : PlannedStmt *stmt = lfirst_node(PlannedStmt, lc2);
2624 109152 : bool canSetTag = stmt->canSetTag;
2625 : DestReceiver *dest;
2626 :
2627 : /*
2628 : * Reset output state. (Note that if a non-SPI receiver is used,
2629 : * _SPI_current->processed will stay zero, and that's what we'll
2630 : * report to the caller. It's the receiver's job to count tuples
2631 : * in that case.)
2632 : */
2633 109152 : _SPI_current->processed = 0;
2634 109152 : _SPI_current->tuptable = NULL;
2635 :
2636 : /* Check for unsupported cases. */
2637 109152 : if (stmt->utilityStmt)
2638 : {
2639 25122 : if (IsA(stmt->utilityStmt, CopyStmt))
2640 : {
2641 18 : CopyStmt *cstmt = (CopyStmt *) stmt->utilityStmt;
2642 :
2643 18 : if (cstmt->filename == NULL)
2644 : {
2645 8 : my_res = SPI_ERROR_COPY;
2646 18 : goto fail;
2647 : }
2648 : }
2649 25104 : else if (IsA(stmt->utilityStmt, TransactionStmt))
2650 : {
2651 10 : my_res = SPI_ERROR_TRANSACTION;
2652 10 : goto fail;
2653 : }
2654 : }
2655 :
2656 109134 : if (options->read_only && !CommandIsReadOnly(stmt))
2657 0 : ereport(ERROR,
2658 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2659 : /* translator: %s is a SQL statement name */
2660 : errmsg("%s is not allowed in a non-volatile function",
2661 : CreateCommandName((Node *) stmt))));
2662 :
2663 : /*
2664 : * If not read-only mode, advance the command counter before each
2665 : * command and update the snapshot. (But skip it if the snapshot
2666 : * isn't under our control.)
2667 : */
2668 109134 : if (!options->read_only && pushed_active_snap)
2669 : {
2670 92320 : CommandCounterIncrement();
2671 92320 : UpdateActiveSnapshotCommandId();
2672 : }
2673 :
2674 : /*
2675 : * Select appropriate tuple receiver. Output from non-canSetTag
2676 : * subqueries always goes to the bit bucket.
2677 : */
2678 109134 : if (!canSetTag)
2679 0 : dest = CreateDestReceiver(DestNone);
2680 109134 : else if (options->dest)
2681 2666 : dest = options->dest;
2682 : else
2683 106468 : dest = CreateDestReceiver(DestSPI);
2684 :
2685 109134 : if (stmt->utilityStmt == NULL)
2686 : {
2687 : QueryDesc *qdesc;
2688 : Snapshot snap;
2689 :
2690 84030 : if (ActiveSnapshotSet())
2691 84030 : snap = GetActiveSnapshot();
2692 : else
2693 0 : snap = InvalidSnapshot;
2694 :
2695 84030 : qdesc = CreateQueryDesc(stmt,
2696 : cplan,
2697 : plansource->query_string,
2698 : snap, crosscheck_snapshot,
2699 : dest,
2700 : options->params,
2701 84030 : _SPI_current->queryEnv,
2702 : 0);
2703 :
2704 84030 : res = _SPI_pquery(qdesc, fire_triggers, canSetTag ? options->tcount : 0,
2705 : plansource, query_index);
2706 78658 : FreeQueryDesc(qdesc);
2707 : }
2708 : else
2709 : {
2710 : ProcessUtilityContext context;
2711 : QueryCompletion qc;
2712 :
2713 : /*
2714 : * If we're not allowing nonatomic operations, tell
2715 : * ProcessUtility this is an atomic execution context.
2716 : */
2717 25104 : if (allow_nonatomic)
2718 102 : context = PROCESS_UTILITY_QUERY_NONATOMIC;
2719 : else
2720 25002 : context = PROCESS_UTILITY_QUERY;
2721 :
2722 25104 : InitializeQueryCompletion(&qc);
2723 25104 : ProcessUtility(stmt,
2724 : plansource->query_string,
2725 : true, /* protect plancache's node tree */
2726 : context,
2727 : options->params,
2728 25104 : _SPI_current->queryEnv,
2729 : dest,
2730 : &qc);
2731 :
2732 : /* Update "processed" if stmt returned tuples */
2733 24978 : if (_SPI_current->tuptable)
2734 152 : _SPI_current->processed = _SPI_current->tuptable->numvals;
2735 :
2736 24978 : res = SPI_OK_UTILITY;
2737 :
2738 : /*
2739 : * Some utility statements return a row count, even though the
2740 : * tuples are not returned to the caller.
2741 : */
2742 24978 : if (IsA(stmt->utilityStmt, CreateTableAsStmt))
2743 : {
2744 50 : CreateTableAsStmt *ctastmt = (CreateTableAsStmt *) stmt->utilityStmt;
2745 :
2746 50 : if (qc.commandTag == CMDTAG_SELECT)
2747 44 : _SPI_current->processed = qc.nprocessed;
2748 : else
2749 : {
2750 : /*
2751 : * Must be an IF NOT EXISTS that did nothing, or a
2752 : * CREATE ... WITH NO DATA.
2753 : */
2754 : Assert(ctastmt->if_not_exists ||
2755 : ctastmt->into->skipData);
2756 6 : _SPI_current->processed = 0;
2757 : }
2758 :
2759 : /*
2760 : * For historical reasons, if CREATE TABLE AS was spelled
2761 : * as SELECT INTO, return a special return code.
2762 : */
2763 50 : if (ctastmt->is_select_into)
2764 0 : res = SPI_OK_SELINTO;
2765 : }
2766 24928 : else if (IsA(stmt->utilityStmt, CopyStmt))
2767 : {
2768 : Assert(qc.commandTag == CMDTAG_COPY);
2769 10 : _SPI_current->processed = qc.nprocessed;
2770 : }
2771 : }
2772 :
2773 : /*
2774 : * The last canSetTag query sets the status values returned to the
2775 : * caller. Be careful to free any tuptables not returned, to
2776 : * avoid intra-transaction memory leak.
2777 : */
2778 103636 : if (canSetTag)
2779 : {
2780 103636 : my_processed = _SPI_current->processed;
2781 103636 : SPI_freetuptable(my_tuptable);
2782 103636 : my_tuptable = _SPI_current->tuptable;
2783 103636 : my_res = res;
2784 : }
2785 : else
2786 : {
2787 0 : SPI_freetuptable(_SPI_current->tuptable);
2788 0 : _SPI_current->tuptable = NULL;
2789 : }
2790 :
2791 : /*
2792 : * We don't issue a destroy call to the receiver. The SPI and
2793 : * None receivers would ignore it anyway, while if the caller
2794 : * supplied a receiver, it's not our job to destroy it.
2795 : */
2796 :
2797 103636 : if (res < 0)
2798 : {
2799 0 : my_res = res;
2800 0 : goto fail;
2801 : }
2802 :
2803 103636 : query_index++;
2804 : }
2805 :
2806 : /* Done with this plan, so release refcount */
2807 103636 : ReleaseCachedPlan(cplan, plan_owner);
2808 103636 : cplan = NULL;
2809 :
2810 : /*
2811 : * If not read-only mode, advance the command counter after the last
2812 : * command. This ensures that its effects are visible, in case it was
2813 : * DDL that would affect the next CachedPlanSource.
2814 : */
2815 103636 : if (!options->read_only)
2816 97726 : CommandCounterIncrement();
2817 : }
2818 :
2819 103654 : fail:
2820 :
2821 : /* Pop the snapshot off the stack if we pushed one */
2822 103654 : if (pushed_active_snap)
2823 87966 : PopActiveSnapshot();
2824 :
2825 : /* We no longer need the cached plan refcount, if any */
2826 103654 : if (cplan)
2827 18 : ReleaseCachedPlan(cplan, plan_owner);
2828 :
2829 : /*
2830 : * Pop the error context stack
2831 : */
2832 103654 : error_context_stack = spierrcontext.previous;
2833 :
2834 : /* Save results for caller */
2835 103654 : SPI_processed = my_processed;
2836 103654 : SPI_tuptable = my_tuptable;
2837 :
2838 : /* tuptable now is caller's responsibility, not SPI's */
2839 103654 : _SPI_current->tuptable = NULL;
2840 :
2841 : /*
2842 : * If none of the queries had canSetTag, return SPI_OK_REWRITTEN. Prior to
2843 : * 8.4, we used return the last query's result code, but not its auxiliary
2844 : * results, but that's confusing.
2845 : */
2846 103654 : if (my_res == 0)
2847 0 : my_res = SPI_OK_REWRITTEN;
2848 :
2849 103654 : return my_res;
2850 : }
2851 :
2852 : /*
2853 : * Convert arrays of query parameters to form wanted by planner and executor
2854 : */
2855 : static ParamListInfo
2856 12648 : _SPI_convert_params(int nargs, Oid *argtypes,
2857 : Datum *Values, const char *Nulls)
2858 : {
2859 : ParamListInfo paramLI;
2860 :
2861 12648 : if (nargs > 0)
2862 : {
2863 11190 : paramLI = makeParamList(nargs);
2864 :
2865 28916 : for (int i = 0; i < nargs; i++)
2866 : {
2867 17726 : ParamExternData *prm = ¶mLI->params[i];
2868 :
2869 17726 : prm->value = Values[i];
2870 17726 : prm->isnull = (Nulls && Nulls[i] == 'n');
2871 17726 : prm->pflags = PARAM_FLAG_CONST;
2872 17726 : prm->ptype = argtypes[i];
2873 : }
2874 : }
2875 : else
2876 1458 : paramLI = NULL;
2877 12648 : return paramLI;
2878 : }
2879 :
2880 : static int
2881 84030 : _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, uint64 tcount,
2882 : CachedPlanSource *plansource, int query_index)
2883 : {
2884 84030 : int operation = queryDesc->operation;
2885 : int eflags;
2886 : int res;
2887 :
2888 84030 : switch (operation)
2889 : {
2890 54390 : case CMD_SELECT:
2891 54390 : if (queryDesc->dest->mydest == DestNone)
2892 : {
2893 : /* Don't return SPI_OK_SELECT if we're discarding result */
2894 0 : res = SPI_OK_UTILITY;
2895 : }
2896 : else
2897 54390 : res = SPI_OK_SELECT;
2898 54390 : break;
2899 19474 : case CMD_INSERT:
2900 19474 : if (queryDesc->plannedstmt->hasReturning)
2901 946 : res = SPI_OK_INSERT_RETURNING;
2902 : else
2903 18528 : res = SPI_OK_INSERT;
2904 19474 : break;
2905 8480 : case CMD_DELETE:
2906 8480 : if (queryDesc->plannedstmt->hasReturning)
2907 0 : res = SPI_OK_DELETE_RETURNING;
2908 : else
2909 8480 : res = SPI_OK_DELETE;
2910 8480 : break;
2911 1620 : case CMD_UPDATE:
2912 1620 : if (queryDesc->plannedstmt->hasReturning)
2913 10 : res = SPI_OK_UPDATE_RETURNING;
2914 : else
2915 1610 : res = SPI_OK_UPDATE;
2916 1620 : break;
2917 66 : case CMD_MERGE:
2918 66 : if (queryDesc->plannedstmt->hasReturning)
2919 18 : res = SPI_OK_MERGE_RETURNING;
2920 : else
2921 48 : res = SPI_OK_MERGE;
2922 66 : break;
2923 0 : default:
2924 0 : return SPI_ERROR_OPUNKNOWN;
2925 : }
2926 :
2927 : #ifdef SPI_EXECUTOR_STATS
2928 : if (ShowExecutorStats)
2929 : ResetUsage();
2930 : #endif
2931 :
2932 : /* Select execution options */
2933 84030 : if (fire_triggers)
2934 76178 : eflags = 0; /* default run-to-completion flags */
2935 : else
2936 7852 : eflags = EXEC_FLAG_SKIP_TRIGGERS;
2937 :
2938 84030 : if (queryDesc->cplan)
2939 : {
2940 84030 : ExecutorStartCachedPlan(queryDesc, eflags, plansource, query_index);
2941 : Assert(queryDesc->planstate);
2942 : }
2943 : else
2944 : {
2945 0 : if (!ExecutorStart(queryDesc, eflags))
2946 0 : elog(ERROR, "ExecutorStart() failed unexpectedly");
2947 : }
2948 :
2949 84030 : ExecutorRun(queryDesc, ForwardScanDirection, tcount);
2950 :
2951 78660 : _SPI_current->processed = queryDesc->estate->es_processed;
2952 :
2953 78660 : if ((res == SPI_OK_SELECT || queryDesc->plannedstmt->hasReturning) &&
2954 50046 : queryDesc->dest->mydest == DestSPI)
2955 : {
2956 47440 : if (_SPI_checktuples())
2957 0 : elog(ERROR, "consistency check on SPI tuple count failed");
2958 : }
2959 :
2960 78660 : ExecutorFinish(queryDesc);
2961 78658 : ExecutorEnd(queryDesc);
2962 : /* FreeQueryDesc is done by the caller */
2963 :
2964 : #ifdef SPI_EXECUTOR_STATS
2965 : if (ShowExecutorStats)
2966 : ShowUsage("SPI EXECUTOR STATS");
2967 : #endif
2968 :
2969 78658 : return res;
2970 : }
2971 :
2972 : /*
2973 : * _SPI_error_callback
2974 : *
2975 : * Add context information when a query invoked via SPI fails
2976 : */
2977 : static void
2978 6474 : _SPI_error_callback(void *arg)
2979 : {
2980 6474 : SPICallbackArg *carg = (SPICallbackArg *) arg;
2981 6474 : const char *query = carg->query;
2982 : int syntaxerrposition;
2983 :
2984 6474 : if (query == NULL) /* in case arg wasn't set yet */
2985 0 : return;
2986 :
2987 : /*
2988 : * If there is a syntax error position, convert to internal syntax error;
2989 : * otherwise treat the query as an item of context stack
2990 : */
2991 6474 : syntaxerrposition = geterrposition();
2992 6474 : if (syntaxerrposition > 0)
2993 : {
2994 108 : errposition(0);
2995 108 : internalerrposition(syntaxerrposition);
2996 108 : internalerrquery(query);
2997 : }
2998 : else
2999 : {
3000 : /* Use the parse mode to decide how to describe the query */
3001 6366 : switch (carg->mode)
3002 : {
3003 64 : case RAW_PARSE_PLPGSQL_EXPR:
3004 64 : errcontext("PL/pgSQL expression \"%s\"", query);
3005 64 : break;
3006 16 : case RAW_PARSE_PLPGSQL_ASSIGN1:
3007 : case RAW_PARSE_PLPGSQL_ASSIGN2:
3008 : case RAW_PARSE_PLPGSQL_ASSIGN3:
3009 16 : errcontext("PL/pgSQL assignment \"%s\"", query);
3010 16 : break;
3011 6286 : default:
3012 6286 : errcontext("SQL statement \"%s\"", query);
3013 6286 : break;
3014 : }
3015 : }
3016 : }
3017 :
3018 : /*
3019 : * _SPI_cursor_operation()
3020 : *
3021 : * Do a FETCH or MOVE in a cursor
3022 : */
3023 : static void
3024 44190 : _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
3025 : DestReceiver *dest)
3026 : {
3027 : uint64 nfetched;
3028 :
3029 : /* Check that the portal is valid */
3030 44190 : if (!PortalIsValid(portal))
3031 0 : elog(ERROR, "invalid portal in SPI cursor operation");
3032 :
3033 : /* Push the SPI stack */
3034 44190 : if (_SPI_begin_call(true) < 0)
3035 0 : elog(ERROR, "SPI cursor operation called while not connected");
3036 :
3037 : /* Reset the SPI result (note we deliberately don't touch lastoid) */
3038 44190 : SPI_processed = 0;
3039 44190 : SPI_tuptable = NULL;
3040 44190 : _SPI_current->processed = 0;
3041 44190 : _SPI_current->tuptable = NULL;
3042 :
3043 : /* Run the cursor */
3044 44190 : nfetched = PortalRunFetch(portal,
3045 : direction,
3046 : count,
3047 : dest);
3048 :
3049 : /*
3050 : * Think not to combine this store with the preceding function call. If
3051 : * the portal contains calls to functions that use SPI, then _SPI_stack is
3052 : * likely to move around while the portal runs. When control returns,
3053 : * _SPI_current will point to the correct stack entry... but the pointer
3054 : * may be different than it was beforehand. So we must be sure to re-fetch
3055 : * the pointer after the function call completes.
3056 : */
3057 44176 : _SPI_current->processed = nfetched;
3058 :
3059 44176 : if (dest->mydest == DestSPI && _SPI_checktuples())
3060 0 : elog(ERROR, "consistency check on SPI tuple count failed");
3061 :
3062 : /* Put the result into place for access by caller */
3063 44176 : SPI_processed = _SPI_current->processed;
3064 44176 : SPI_tuptable = _SPI_current->tuptable;
3065 :
3066 : /* tuptable now is caller's responsibility, not SPI's */
3067 44176 : _SPI_current->tuptable = NULL;
3068 :
3069 : /* Pop the SPI stack */
3070 44176 : _SPI_end_call(true);
3071 44176 : }
3072 :
3073 :
3074 : static MemoryContext
3075 208428 : _SPI_execmem(void)
3076 : {
3077 208428 : return MemoryContextSwitchTo(_SPI_current->execCxt);
3078 : }
3079 :
3080 : static MemoryContext
3081 299682 : _SPI_procmem(void)
3082 : {
3083 299682 : return MemoryContextSwitchTo(_SPI_current->procCxt);
3084 : }
3085 :
3086 : /*
3087 : * _SPI_begin_call: begin a SPI operation within a connected procedure
3088 : *
3089 : * use_exec is true if we intend to make use of the procedure's execCxt
3090 : * during this SPI operation. We'll switch into that context, and arrange
3091 : * for it to be cleaned up at _SPI_end_call or if an error occurs.
3092 : */
3093 : static int
3094 310622 : _SPI_begin_call(bool use_exec)
3095 : {
3096 310622 : if (_SPI_current == NULL)
3097 0 : return SPI_ERROR_UNCONNECTED;
3098 :
3099 310622 : if (use_exec)
3100 : {
3101 : /* remember when the Executor operation started */
3102 208428 : _SPI_current->execSubid = GetCurrentSubTransactionId();
3103 : /* switch to the Executor memory context */
3104 208428 : _SPI_execmem();
3105 : }
3106 :
3107 310622 : return 0;
3108 : }
3109 :
3110 : /*
3111 : * _SPI_end_call: end a SPI operation within a connected procedure
3112 : *
3113 : * use_exec must be the same as in the previous _SPI_begin_call
3114 : *
3115 : * Note: this currently has no failure return cases, so callers don't check
3116 : */
3117 : static int
3118 203350 : _SPI_end_call(bool use_exec)
3119 : {
3120 203350 : if (use_exec)
3121 : {
3122 : /* switch to the procedure memory context */
3123 202636 : _SPI_procmem();
3124 : /* mark Executor context no longer in use */
3125 202636 : _SPI_current->execSubid = InvalidSubTransactionId;
3126 : /* and free Executor memory */
3127 202636 : MemoryContextReset(_SPI_current->execCxt);
3128 : }
3129 :
3130 203350 : return 0;
3131 : }
3132 :
3133 : static bool
3134 91574 : _SPI_checktuples(void)
3135 : {
3136 91574 : uint64 processed = _SPI_current->processed;
3137 91574 : SPITupleTable *tuptable = _SPI_current->tuptable;
3138 91574 : bool failed = false;
3139 :
3140 91574 : if (tuptable == NULL) /* spi_dest_startup was not called */
3141 0 : failed = true;
3142 91574 : else if (processed != tuptable->numvals)
3143 0 : failed = true;
3144 :
3145 91574 : return failed;
3146 : }
3147 :
3148 : /*
3149 : * Convert a "temporary" SPIPlan into an "unsaved" plan.
3150 : *
3151 : * The passed _SPI_plan struct is on the stack, and all its subsidiary data
3152 : * is in or under the current SPI executor context. Copy the plan into the
3153 : * SPI procedure context so it will survive _SPI_end_call(). To minimize
3154 : * data copying, this destructively modifies the input plan, by taking the
3155 : * plancache entries away from it and reparenting them to the new SPIPlan.
3156 : */
3157 : static SPIPlanPtr
3158 33202 : _SPI_make_plan_non_temp(SPIPlanPtr plan)
3159 : {
3160 : SPIPlanPtr newplan;
3161 33202 : MemoryContext parentcxt = _SPI_current->procCxt;
3162 : MemoryContext plancxt;
3163 : MemoryContext oldcxt;
3164 : ListCell *lc;
3165 :
3166 : /* Assert the input is a temporary SPIPlan */
3167 : Assert(plan->magic == _SPI_PLAN_MAGIC);
3168 : Assert(plan->plancxt == NULL);
3169 : /* One-shot plans can't be saved */
3170 : Assert(!plan->oneshot);
3171 :
3172 : /*
3173 : * Create a memory context for the plan, underneath the procedure context.
3174 : * We don't expect the plan to be very large.
3175 : */
3176 33202 : plancxt = AllocSetContextCreate(parentcxt,
3177 : "SPI Plan",
3178 : ALLOCSET_SMALL_SIZES);
3179 33202 : oldcxt = MemoryContextSwitchTo(plancxt);
3180 :
3181 : /* Copy the _SPI_plan struct and subsidiary data into the new context */
3182 33202 : newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
3183 33202 : newplan->magic = _SPI_PLAN_MAGIC;
3184 33202 : newplan->plancxt = plancxt;
3185 33202 : newplan->parse_mode = plan->parse_mode;
3186 33202 : newplan->cursor_options = plan->cursor_options;
3187 33202 : newplan->nargs = plan->nargs;
3188 33202 : if (plan->nargs > 0)
3189 : {
3190 3798 : newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
3191 3798 : memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
3192 : }
3193 : else
3194 29404 : newplan->argtypes = NULL;
3195 33202 : newplan->parserSetup = plan->parserSetup;
3196 33202 : newplan->parserSetupArg = plan->parserSetupArg;
3197 :
3198 : /*
3199 : * Reparent all the CachedPlanSources into the procedure context. In
3200 : * theory this could fail partway through due to the pallocs, but we don't
3201 : * care too much since both the procedure context and the executor context
3202 : * would go away on error.
3203 : */
3204 66404 : foreach(lc, plan->plancache_list)
3205 : {
3206 33202 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3207 :
3208 33202 : CachedPlanSetParentContext(plansource, parentcxt);
3209 :
3210 : /* Build new list, with list cells in plancxt */
3211 33202 : newplan->plancache_list = lappend(newplan->plancache_list, plansource);
3212 : }
3213 :
3214 33202 : MemoryContextSwitchTo(oldcxt);
3215 :
3216 : /* For safety, unlink the CachedPlanSources from the temporary plan */
3217 33202 : plan->plancache_list = NIL;
3218 :
3219 33202 : return newplan;
3220 : }
3221 :
3222 : /*
3223 : * Make a "saved" copy of the given plan.
3224 : */
3225 : static SPIPlanPtr
3226 0 : _SPI_save_plan(SPIPlanPtr plan)
3227 : {
3228 : SPIPlanPtr newplan;
3229 : MemoryContext plancxt;
3230 : MemoryContext oldcxt;
3231 : ListCell *lc;
3232 :
3233 : /* One-shot plans can't be saved */
3234 : Assert(!plan->oneshot);
3235 :
3236 : /*
3237 : * Create a memory context for the plan. We don't expect the plan to be
3238 : * very large, so use smaller-than-default alloc parameters. It's a
3239 : * transient context until we finish copying everything.
3240 : */
3241 0 : plancxt = AllocSetContextCreate(CurrentMemoryContext,
3242 : "SPI Plan",
3243 : ALLOCSET_SMALL_SIZES);
3244 0 : oldcxt = MemoryContextSwitchTo(plancxt);
3245 :
3246 : /* Copy the SPI plan into its own context */
3247 0 : newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
3248 0 : newplan->magic = _SPI_PLAN_MAGIC;
3249 0 : newplan->plancxt = plancxt;
3250 0 : newplan->parse_mode = plan->parse_mode;
3251 0 : newplan->cursor_options = plan->cursor_options;
3252 0 : newplan->nargs = plan->nargs;
3253 0 : if (plan->nargs > 0)
3254 : {
3255 0 : newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
3256 0 : memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
3257 : }
3258 : else
3259 0 : newplan->argtypes = NULL;
3260 0 : newplan->parserSetup = plan->parserSetup;
3261 0 : newplan->parserSetupArg = plan->parserSetupArg;
3262 :
3263 : /* Copy all the plancache entries */
3264 0 : foreach(lc, plan->plancache_list)
3265 : {
3266 0 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3267 : CachedPlanSource *newsource;
3268 :
3269 0 : newsource = CopyCachedPlan(plansource);
3270 0 : newplan->plancache_list = lappend(newplan->plancache_list, newsource);
3271 : }
3272 :
3273 0 : MemoryContextSwitchTo(oldcxt);
3274 :
3275 : /*
3276 : * Mark it saved, reparent it under CacheMemoryContext, and mark all the
3277 : * component CachedPlanSources as saved. This sequence cannot fail
3278 : * partway through, so there's no risk of long-term memory leakage.
3279 : */
3280 0 : newplan->saved = true;
3281 0 : MemoryContextSetParent(newplan->plancxt, CacheMemoryContext);
3282 :
3283 0 : foreach(lc, newplan->plancache_list)
3284 : {
3285 0 : CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
3286 :
3287 0 : SaveCachedPlan(plansource);
3288 : }
3289 :
3290 0 : return newplan;
3291 : }
3292 :
3293 : /*
3294 : * Internal lookup of ephemeral named relation by name.
3295 : */
3296 : static EphemeralNamedRelation
3297 714 : _SPI_find_ENR_by_name(const char *name)
3298 : {
3299 : /* internal static function; any error is bug in SPI itself */
3300 : Assert(name != NULL);
3301 :
3302 : /* fast exit if no tuplestores have been added */
3303 714 : if (_SPI_current->queryEnv == NULL)
3304 564 : return NULL;
3305 :
3306 150 : return get_ENR(_SPI_current->queryEnv, name);
3307 : }
3308 :
3309 : /*
3310 : * Register an ephemeral named relation for use by the planner and executor on
3311 : * subsequent calls using this SPI connection.
3312 : */
3313 : int
3314 714 : SPI_register_relation(EphemeralNamedRelation enr)
3315 : {
3316 : EphemeralNamedRelation match;
3317 : int res;
3318 :
3319 714 : if (enr == NULL || enr->md.name == NULL)
3320 0 : return SPI_ERROR_ARGUMENT;
3321 :
3322 714 : res = _SPI_begin_call(false); /* keep current memory context */
3323 714 : if (res < 0)
3324 0 : return res;
3325 :
3326 714 : match = _SPI_find_ENR_by_name(enr->md.name);
3327 714 : if (match)
3328 0 : res = SPI_ERROR_REL_DUPLICATE;
3329 : else
3330 : {
3331 714 : if (_SPI_current->queryEnv == NULL)
3332 564 : _SPI_current->queryEnv = create_queryEnv();
3333 :
3334 714 : register_ENR(_SPI_current->queryEnv, enr);
3335 714 : res = SPI_OK_REL_REGISTER;
3336 : }
3337 :
3338 714 : _SPI_end_call(false);
3339 :
3340 714 : return res;
3341 : }
3342 :
3343 : /*
3344 : * Unregister an ephemeral named relation by name. This will probably be a
3345 : * rarely used function, since SPI_finish will clear it automatically.
3346 : */
3347 : int
3348 0 : SPI_unregister_relation(const char *name)
3349 : {
3350 : EphemeralNamedRelation match;
3351 : int res;
3352 :
3353 0 : if (name == NULL)
3354 0 : return SPI_ERROR_ARGUMENT;
3355 :
3356 0 : res = _SPI_begin_call(false); /* keep current memory context */
3357 0 : if (res < 0)
3358 0 : return res;
3359 :
3360 0 : match = _SPI_find_ENR_by_name(name);
3361 0 : if (match)
3362 : {
3363 0 : unregister_ENR(_SPI_current->queryEnv, match->md.name);
3364 0 : res = SPI_OK_REL_UNREGISTER;
3365 : }
3366 : else
3367 0 : res = SPI_ERROR_REL_NOT_FOUND;
3368 :
3369 0 : _SPI_end_call(false);
3370 :
3371 0 : return res;
3372 : }
3373 :
3374 : /*
3375 : * Register the transient relations from 'tdata' using this SPI connection.
3376 : * This should be called by PL implementations' trigger handlers after
3377 : * connecting, in order to make transition tables visible to any queries run
3378 : * in this connection.
3379 : */
3380 : int
3381 15456 : SPI_register_trigger_data(TriggerData *tdata)
3382 : {
3383 15456 : if (tdata == NULL)
3384 0 : return SPI_ERROR_ARGUMENT;
3385 :
3386 15456 : if (tdata->tg_newtable)
3387 : {
3388 : EphemeralNamedRelation enr =
3389 402 : palloc(sizeof(EphemeralNamedRelationData));
3390 : int rc;
3391 :
3392 402 : enr->md.name = tdata->tg_trigger->tgnewtable;
3393 402 : enr->md.reliddesc = tdata->tg_relation->rd_id;
3394 402 : enr->md.tupdesc = NULL;
3395 402 : enr->md.enrtype = ENR_NAMED_TUPLESTORE;
3396 402 : enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_newtable);
3397 402 : enr->reldata = tdata->tg_newtable;
3398 402 : rc = SPI_register_relation(enr);
3399 402 : if (rc != SPI_OK_REL_REGISTER)
3400 0 : return rc;
3401 : }
3402 :
3403 15456 : if (tdata->tg_oldtable)
3404 : {
3405 : EphemeralNamedRelation enr =
3406 312 : palloc(sizeof(EphemeralNamedRelationData));
3407 : int rc;
3408 :
3409 312 : enr->md.name = tdata->tg_trigger->tgoldtable;
3410 312 : enr->md.reliddesc = tdata->tg_relation->rd_id;
3411 312 : enr->md.tupdesc = NULL;
3412 312 : enr->md.enrtype = ENR_NAMED_TUPLESTORE;
3413 312 : enr->md.enrtuples = tuplestore_tuple_count(tdata->tg_oldtable);
3414 312 : enr->reldata = tdata->tg_oldtable;
3415 312 : rc = SPI_register_relation(enr);
3416 312 : if (rc != SPI_OK_REL_REGISTER)
3417 0 : return rc;
3418 : }
3419 :
3420 15456 : return SPI_OK_TD_REGISTER;
3421 : }
|