1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

PL/pgSQL: Nested CALL with transactions

So far, a nested CALL or DO in PL/pgSQL would not establish a context
where transaction control statements were allowed.  This fixes that by
handling CALL and DO specially in PL/pgSQL, passing the atomic/nonatomic
execution context through and doing the required management around
transaction boundaries.

Reviewed-by: Tomas Vondra <tomas.vondra@2ndquadrant.com>
This commit is contained in:
Peter Eisentraut
2018-03-24 10:05:06 -04:00
parent c2d4eb1b1f
commit d92bc83c48
13 changed files with 235 additions and 59 deletions

View File

@ -2041,8 +2041,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
*
* In the first two cases, we can just push the snap onto the stack once
* for the whole plan list.
*
* But if the plan has no_snapshots set to true, then don't manage
* snapshots at all. The caller should then take care of that.
*/
if (snapshot != InvalidSnapshot)
if (snapshot != InvalidSnapshot && !plan->no_snapshots)
{
if (read_only)
{
@ -2121,7 +2124,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
* In the default non-read-only case, get a new snapshot, replacing
* any that we pushed in a previous cycle.
*/
if (snapshot == InvalidSnapshot && !read_only)
if (snapshot == InvalidSnapshot && !read_only && !plan->no_snapshots)
{
if (pushed_active_snap)
PopActiveSnapshot();
@ -2172,7 +2175,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
* If not read-only mode, advance the command counter before each
* command and update the snapshot.
*/
if (!read_only)
if (!read_only && !plan->no_snapshots)
{
CommandCounterIncrement();
UpdateActiveSnapshotCommandId();
@ -2203,10 +2206,23 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
else
{
char completionTag[COMPLETION_TAG_BUFSIZE];
ProcessUtilityContext context;
/*
* If the SPI context is atomic, or we are asked to manage
* snapshots, then we are in an atomic execution context.
* Conversely, to propagate a nonatomic execution context, the
* caller must be in a nonatomic SPI context and manage
* snapshots itself.
*/
if (_SPI_current->atomic || !plan->no_snapshots)
context = PROCESS_UTILITY_QUERY;
else
context = PROCESS_UTILITY_QUERY_NONATOMIC;
ProcessUtility(stmt,
plansource->query_string,
PROCESS_UTILITY_QUERY,
context,
paramLI,
_SPI_current->queryEnv,
dest,
@ -2638,11 +2654,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
oldcxt = MemoryContextSwitchTo(plancxt);
/* Copy the SPI_plan struct and subsidiary data into the new context */
newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
newplan->magic = _SPI_PLAN_MAGIC;
newplan->saved = false;
newplan->oneshot = false;
newplan->plancache_list = NIL;
newplan->plancxt = plancxt;
newplan->cursor_options = plan->cursor_options;
newplan->nargs = plan->nargs;
@ -2705,11 +2718,8 @@ _SPI_save_plan(SPIPlanPtr plan)
oldcxt = MemoryContextSwitchTo(plancxt);
/* Copy the SPI plan into its own context */
newplan = (SPIPlanPtr) palloc(sizeof(_SPI_plan));
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
newplan->magic = _SPI_PLAN_MAGIC;
newplan->saved = false;
newplan->oneshot = false;
newplan->plancache_list = NIL;
newplan->plancxt = plancxt;
newplan->cursor_options = plan->cursor_options;
newplan->nargs = plan->nargs;