1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Nested transactions. There is still much left to do, especially on the

performance front, but with feature freeze upon us I think it's time to
drive a stake in the ground and say that this will be in 7.5.

Alvaro Herrera, with some help from Tom Lane.
This commit is contained in:
Tom Lane
2004-07-01 00:52:04 +00:00
parent 4c9aa572fa
commit 573a71a5da
74 changed files with 4516 additions and 1144 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.118 2004/06/11 01:08:43 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.119 2004/07/01 00:50:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,6 +29,7 @@ int SPI_result;
static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = NULL;
static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
static int _SPI_connected = -1;
static int _SPI_curid = -1;
@ -59,7 +60,7 @@ static bool _SPI_checktuples(void);
int
SPI_connect(void)
{
_SPI_connection *new_SPI_stack;
int newdepth;
/*
* When procedure called by Executor _SPI_curid expected to be equal
@ -70,39 +71,46 @@ SPI_connect(void)
if (_SPI_stack == NULL)
{
if (_SPI_connected != -1)
if (_SPI_connected != -1 || _SPI_stack_depth != 0)
elog(ERROR, "SPI stack corrupted");
new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
newdepth = 16;
_SPI_stack = (_SPI_connection *)
MemoryContextAlloc(TopTransactionContext,
newdepth * sizeof(_SPI_connection));
_SPI_stack_depth = newdepth;
}
else
{
if (_SPI_connected < 0)
if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
elog(ERROR, "SPI stack corrupted");
new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
(_SPI_connected + 2) * sizeof(_SPI_connection));
if (_SPI_stack_depth == _SPI_connected + 1)
{
newdepth = _SPI_stack_depth * 2;
_SPI_stack = (_SPI_connection *)
repalloc(_SPI_stack,
newdepth * sizeof(_SPI_connection));
_SPI_stack_depth = newdepth;
}
}
if (new_SPI_stack == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
/*
* We' returning to procedure where _SPI_curid == _SPI_connected - 1
* We're entering procedure where _SPI_curid == _SPI_connected - 1
*/
_SPI_stack = new_SPI_stack;
_SPI_connected++;
Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
_SPI_current = &(_SPI_stack[_SPI_connected]);
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
_SPI_current->connectXid = GetCurrentTransactionId();
/*
* Create memory contexts for this procedure
*
* XXX it would be better to use PortalContext as the parent context, but
* we may not be inside a portal (consider deferred-trigger
* execution).
* XXX it would be better to use PortalContext as the parent context,
* but we may not be inside a portal (consider deferred-trigger
* execution). Perhaps CurTransactionContext would do? For now it
* doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
*/
_SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
"SPI Proc",
@ -152,28 +160,11 @@ SPI_finish(void)
_SPI_connected--;
_SPI_curid--;
if (_SPI_connected == -1)
{
free(_SPI_stack);
_SPI_stack = NULL;
_SPI_current = NULL;
}
else
{
_SPI_connection *new_SPI_stack;
new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
(_SPI_connected + 1) * sizeof(_SPI_connection));
/* This could only fail with a pretty stupid malloc package ... */
if (new_SPI_stack == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
_SPI_stack = new_SPI_stack;
_SPI_current = &(_SPI_stack[_SPI_connected]);
}
return SPI_OK_FINISH;
}
/*
@ -187,23 +178,54 @@ AtEOXact_SPI(bool isCommit)
* freed automatically, so we can ignore them here. We just need to
* restore our static variables to initial state.
*/
if (_SPI_stack != NULL)
{
free(_SPI_stack);
if (isCommit)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("freeing non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
}
if (isCommit && _SPI_connected != -1)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("transaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
_SPI_current = _SPI_stack = NULL;
_SPI_stack_depth = 0;
_SPI_connected = _SPI_curid = -1;
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
}
/*
* Clean up SPI state at subtransaction commit or abort.
*
* During commit, there shouldn't be any unclosed entries remaining from
* the current transaction; we throw them away if found.
*/
void
AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
{
bool found = false;
while (_SPI_connected >= 0)
{
_SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
int res;
if (connection->connectXid != childXid)
break; /* couldn't be any underneath it either */
found = true;
_SPI_curid = _SPI_connected - 1; /* avoid begin_call error */
res = SPI_finish();
Assert(res == SPI_OK_FINISH);
}
if (found && isCommit)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("subtransaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
}
/* Pushes SPI stack to allow recursive SPI calls */
void
SPI_push(void)
@ -1148,16 +1170,18 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = SPI_ERROR_CURSOR;
goto fail;
}
else if (IsA(queryTree->utilityStmt, TransactionStmt))
{
res = SPI_ERROR_TRANSACTION;
goto fail;
}
res = SPI_OK_UTILITY;
if (plan == NULL)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
CommandCounterIncrement();
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
}
}
else if (plan == NULL)
@ -1273,7 +1297,14 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
res = SPI_OK_UTILITY;
CommandCounterIncrement();
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
}
else
{