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