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