Commit 8f9f1986 authored by Tom Lane's avatar Tom Lane

Restructure subtransaction handling to reduce resource consumption,

as per recent discussions.  Invent SubTransactionIds that are managed like
CommandIds (ie, counter is reset at start of each top transaction), and
use these instead of TransactionIds to keep track of subtransaction status
in those modules that need it.  This means that a subtransaction does not
need an XID unless it actually inserts/modifies rows in the database.
Accordingly, don't assign it an XID nor take a lock on the XID until it
tries to do that.  This saves a lot of overhead for subtransactions that
are only used for error recovery (eg plpgsql exceptions).  Also, arrange
to release a subtransaction's XID lock as soon as the subtransaction
exits, in both the commit and abort cases.  This avoids holding many
unique locks after a long series of subtransactions.  The price is some
additional overhead in XactLockTableWait, but that seems acceptable.
Finally, restructure the state machine in xact.c to have a more orthogonal
set of states for subtransactions.
parent 42c0d1f3
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.174 2004/09/11 18:28:32 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.175 2004/09/16 16:58:25 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1108,6 +1108,7 @@ heap_get_latest_tid(Relation relation, ...@@ -1108,6 +1108,7 @@ heap_get_latest_tid(Relation relation,
Oid Oid
heap_insert(Relation relation, HeapTuple tup, CommandId cid) heap_insert(Relation relation, HeapTuple tup, CommandId cid)
{ {
TransactionId xid = GetCurrentTransactionId();
Buffer buffer; Buffer buffer;
if (relation->rd_rel->relhasoids) if (relation->rd_rel->relhasoids)
...@@ -1139,7 +1140,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid) ...@@ -1139,7 +1140,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID; tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmin(tup->t_data, xid);
HeapTupleHeaderSetCmin(tup->t_data, cid); HeapTupleHeaderSetCmin(tup->t_data, cid);
HeapTupleHeaderSetCmax(tup->t_data, 0); /* zero out Datum fields */ HeapTupleHeaderSetCmax(tup->t_data, 0); /* zero out Datum fields */
tup->t_tableOid = relation->rd_id; tup->t_tableOid = relation->rd_id;
...@@ -1277,6 +1278,7 @@ heap_delete(Relation relation, ItemPointer tid, ...@@ -1277,6 +1278,7 @@ heap_delete(Relation relation, ItemPointer tid,
ItemPointer ctid, CommandId cid, ItemPointer ctid, CommandId cid,
Snapshot crosscheck, bool wait) Snapshot crosscheck, bool wait)
{ {
TransactionId xid = GetCurrentTransactionId();
ItemId lp; ItemId lp;
HeapTupleData tp; HeapTupleData tp;
PageHeader dp; PageHeader dp;
...@@ -1365,7 +1367,7 @@ l1: ...@@ -1365,7 +1367,7 @@ l1:
HEAP_XMAX_INVALID | HEAP_XMAX_INVALID |
HEAP_MARKED_FOR_UPDATE | HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(tp.t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmax(tp.t_data, xid);
HeapTupleHeaderSetCmax(tp.t_data, cid); HeapTupleHeaderSetCmax(tp.t_data, cid);
/* Make sure there is no forward chain link in t_ctid */ /* Make sure there is no forward chain link in t_ctid */
tp.t_data->t_ctid = tp.t_self; tp.t_data->t_ctid = tp.t_self;
...@@ -1495,6 +1497,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, ...@@ -1495,6 +1497,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
ItemPointer ctid, CommandId cid, ItemPointer ctid, CommandId cid,
Snapshot crosscheck, bool wait) Snapshot crosscheck, bool wait)
{ {
TransactionId xid = GetCurrentTransactionId();
ItemId lp; ItemId lp;
HeapTupleData oldtup; HeapTupleData oldtup;
PageHeader dp; PageHeader dp;
...@@ -1603,7 +1606,7 @@ l2: ...@@ -1603,7 +1606,7 @@ l2:
newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
HeapTupleHeaderSetXmin(newtup->t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmin(newtup->t_data, xid);
HeapTupleHeaderSetCmin(newtup->t_data, cid); HeapTupleHeaderSetCmin(newtup->t_data, cid);
HeapTupleHeaderSetCmax(newtup->t_data, 0); /* zero out Datum fields */ HeapTupleHeaderSetCmax(newtup->t_data, 0); /* zero out Datum fields */
...@@ -1644,7 +1647,7 @@ l2: ...@@ -1644,7 +1647,7 @@ l2:
HEAP_MARKED_FOR_UPDATE | HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED); HEAP_MOVED);
oldtup.t_data->t_infomask |= HEAP_XMAX_UNLOGGED; oldtup.t_data->t_infomask |= HEAP_XMAX_UNLOGGED;
HeapTupleHeaderSetXmax(oldtup.t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid); HeapTupleHeaderSetCmax(oldtup.t_data, cid);
already_marked = true; already_marked = true;
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
...@@ -1735,7 +1738,7 @@ l2: ...@@ -1735,7 +1738,7 @@ l2:
HEAP_XMAX_INVALID | HEAP_XMAX_INVALID |
HEAP_MARKED_FOR_UPDATE | HEAP_MARKED_FOR_UPDATE |
HEAP_MOVED); HEAP_MOVED);
HeapTupleHeaderSetXmax(oldtup.t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmax(oldtup.t_data, xid);
HeapTupleHeaderSetCmax(oldtup.t_data, cid); HeapTupleHeaderSetCmax(oldtup.t_data, cid);
} }
...@@ -1836,6 +1839,7 @@ int ...@@ -1836,6 +1839,7 @@ int
heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer, heap_mark4update(Relation relation, HeapTuple tuple, Buffer *buffer,
CommandId cid) CommandId cid)
{ {
TransactionId xid = GetCurrentTransactionId();
ItemPointer tid = &(tuple->t_self); ItemPointer tid = &(tuple->t_self);
ItemId lp; ItemId lp;
PageHeader dp; PageHeader dp;
...@@ -1912,7 +1916,7 @@ l3: ...@@ -1912,7 +1916,7 @@ l3:
HEAP_XMAX_INVALID | HEAP_XMAX_INVALID |
HEAP_MOVED); HEAP_MOVED);
tuple->t_data->t_infomask |= HEAP_MARKED_FOR_UPDATE; tuple->t_data->t_infomask |= HEAP_MARKED_FOR_UPDATE;
HeapTupleHeaderSetXmax(tuple->t_data, GetCurrentTransactionId()); HeapTupleHeaderSetXmax(tuple->t_data, xid);
HeapTupleHeaderSetCmax(tuple->t_data, cid); HeapTupleHeaderSetCmax(tuple->t_data, cid);
/* Make sure there is no forward chain link in t_ctid */ /* Make sure there is no forward chain link in t_ctid */
tuple->t_data->t_ctid = *tid; tuple->t_data->t_ctid = *tid;
...@@ -2584,6 +2588,7 @@ newsame:; ...@@ -2584,6 +2588,7 @@ newsame:;
static void static void
_heap_unlock_tuple(void *data) _heap_unlock_tuple(void *data)
{ {
TransactionId xid = GetCurrentTransactionId();
xl_heaptid *xltid = (xl_heaptid *) data; xl_heaptid *xltid = (xl_heaptid *) data;
Relation reln = XLogOpenRelation(false, RM_HEAP_ID, xltid->node); Relation reln = XLogOpenRelation(false, RM_HEAP_ID, xltid->node);
Buffer buffer; Buffer buffer;
...@@ -2614,13 +2619,12 @@ _heap_unlock_tuple(void *data) ...@@ -2614,13 +2619,12 @@ _heap_unlock_tuple(void *data)
htup = (HeapTupleHeader) PageGetItem(page, lp); htup = (HeapTupleHeader) PageGetItem(page, lp);
if (!TransactionIdEquals(HeapTupleHeaderGetXmax(htup), GetCurrentTransactionId())) if (!TransactionIdEquals(HeapTupleHeaderGetXmax(htup), xid))
elog(PANIC, "_heap_unlock_tuple: invalid xmax in rollback"); elog(PANIC, "_heap_unlock_tuple: invalid xmax in rollback");
htup->t_infomask &= ~HEAP_XMAX_UNLOGGED; htup->t_infomask &= ~HEAP_XMAX_UNLOGGED;
htup->t_infomask |= HEAP_XMAX_INVALID; htup->t_infomask |= HEAP_XMAX_INVALID;
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
WriteBuffer(buffer); WriteBuffer(buffer);
return;
} }
void void
......
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.1 2004/08/01 20:57:59 tgl Exp $ $PostgreSQL: pgsql/src/backend/access/transam/README,v 1.2 2004/09/16 16:58:26 tgl Exp $
The Transaction System The Transaction System
---------------------- ----------------------
...@@ -9,7 +9,7 @@ the mainloop's control code, which in turn implements user-visible ...@@ -9,7 +9,7 @@ the mainloop's control code, which in turn implements user-visible
transactions and savepoints. transactions and savepoints.
The middle layer of code is called by postgres.c before and after the The middle layer of code is called by postgres.c before and after the
processing of each query: processing of each query, or after detecting an error:
StartTransactionCommand StartTransactionCommand
CommitTransactionCommand CommitTransactionCommand
...@@ -44,9 +44,9 @@ effects of previous commands within the same transaction. Note that this is ...@@ -44,9 +44,9 @@ effects of previous commands within the same transaction. Note that this is
done automatically by CommitTransactionCommand after each query inside a done automatically by CommitTransactionCommand after each query inside a
transaction block, but some utility functions also do it internally to allow transaction block, but some utility functions also do it internally to allow
some operations (usually in the system catalogs) to be seen by future some operations (usually in the system catalogs) to be seen by future
operations in the same utility command (for example, in DefineRelation it is operations in the same utility command. (For example, in DefineRelation it is
done after creating the heap so the pg_class row is visible, to be able to done after creating the heap so the pg_class row is visible, to be able to
lock it). lock it.)
For example, consider the following sequence of user commands: For example, consider the following sequence of user commands:
...@@ -60,26 +60,26 @@ In the main processing loop, this results in the following function call ...@@ -60,26 +60,26 @@ In the main processing loop, this results in the following function call
sequence: sequence:
/ StartTransactionCommand; / StartTransactionCommand;
/ ProcessUtility; << BEGIN / StartTransaction;
1) < BeginTransactionBlock; 1) < ProcessUtility; << BEGIN
\ BeginTransactionBlock;
\ CommitTransactionCommand; \ CommitTransactionCommand;
\ StartTransaction;
/ StartTransactionCommand; / StartTransactionCommand;
2) / ProcessQuery; << SELECT * FROM foo 2) / ProcessQuery; << SELECT ...
\ CommitTransactionCommand; \ CommitTransactionCommand;
\ CommandCounterIncrement; \ CommandCounterIncrement;
/ StartTransactionCommand; / StartTransactionCommand;
3) / ProcessQuery; << INSERT INTO foo VALUES (...) 3) / ProcessQuery; << INSERT ...
\ CommitTransactionCommand; \ CommitTransactionCommand;
\ CommandCounterIncrement; \ CommandCounterIncrement;
/ StartTransactionCommand; / StartTransactionCommand;
/ ProcessUtility; << COMMIT / ProcessUtility; << COMMIT
4) < EndTransactionBlock; 4) < EndTransactionBlock;
\ CommitTransaction;
\ CommitTransactionCommand; \ CommitTransactionCommand;
\ CommitTransaction;
The point of this example is to demonstrate the need for The point of this example is to demonstrate the need for
StartTransactionCommand and CommitTransactionCommand to be state smart -- they StartTransactionCommand and CommitTransactionCommand to be state smart -- they
...@@ -118,7 +118,7 @@ to do all the real work. The only difference is what state we enter after ...@@ -118,7 +118,7 @@ to do all the real work. The only difference is what state we enter after
AbortTransaction does its work: AbortTransaction does its work:
* AbortCurrentTransaction leaves us in TBLOCK_ABORT, * AbortCurrentTransaction leaves us in TBLOCK_ABORT,
* UserAbortTransactionBlock leaves us in TBLOCK_ENDABORT * UserAbortTransactionBlock leaves us in TBLOCK_ABORT_END
Low-level transaction abort handling is divided in two phases: Low-level transaction abort handling is divided in two phases:
* AbortTransaction executes as soon as we realize the transaction has * AbortTransaction executes as soon as we realize the transaction has
...@@ -126,7 +126,7 @@ Low-level transaction abort handling is divided in two phases: ...@@ -126,7 +126,7 @@ Low-level transaction abort handling is divided in two phases:
not delay other backends unnecessarily. not delay other backends unnecessarily.
* CleanupTransaction executes when we finally see a user COMMIT * CleanupTransaction executes when we finally see a user COMMIT
or ROLLBACK command; it cleans things up and gets us out of the transaction or ROLLBACK command; it cleans things up and gets us out of the transaction
internally. In particular, we mustn't destroy TopTransactionContext until completely. In particular, we mustn't destroy TopTransactionContext until
this point. this point.
Also, note that when a transaction is committed, we don't close it right away. Also, note that when a transaction is committed, we don't close it right away.
...@@ -163,28 +163,48 @@ called so the system returns to the parent transaction. ...@@ -163,28 +163,48 @@ called so the system returns to the parent transaction.
One important point regarding subtransaction handling is that several may need One important point regarding subtransaction handling is that several may need
to be closed in response to a single user command. That's because savepoints to be closed in response to a single user command. That's because savepoints
have names, and we allow to commit or rollback a savepoint by name, which is have names, and we allow to commit or rollback a savepoint by name, which is
not necessarily the one that was last opened. In the case of subtransaction not necessarily the one that was last opened. Also a COMMIT or ROLLBACK
commit this is not a problem, and we close all the involved subtransactions command must be able to close out the entire stack. We handle this by having
right away by calling CommitTransactionToLevel, which in turn calls the utility command subroutine mark all the state stack entries as commit-
CommitSubTransaction and PopTransaction as many times as needed. pending or abort-pending, and then when the main loop reaches
CommitTransactionCommand, the real work is done. The main point of doing
In the case of subtransaction abort (when the user issues ROLLBACK TO things this way is that if we get an error while popping state stack entries,
<savepoint>), things are not so easy. We have to keep the subtransactions the remaining stack entries still show what we need to do to finish up.
open and return control to the main loop. So what RollbackToSavepoint does is
abort the innermost subtransaction and put it in TBLOCK_SUBENDABORT state, and In the case of ROLLBACK TO <savepoint>, we abort all the subtransactions up
put the rest in TBLOCK_SUBABORT_PENDING state. Then we return control to the through the one identified by the savepoint name, and then re-create that
main loop, which will in turn return control to us by calling subtransaction level with the same name. So it's a completely new
CommitTransactionCommand. At this point we can close all subtransactions that subtransaction as far as the internals are concerned.
are marked with the "abort pending" state. When that's done, the outermost
subtransaction is created again, to conform to SQL's definition of ROLLBACK TO.
Other subsystems are allowed to start "internal" subtransactions, which are Other subsystems are allowed to start "internal" subtransactions, which are
handled by BeginInternalSubtransaction. This is to allow implementing handled by BeginInternalSubtransaction. This is to allow implementing
exception handling, e.g. in PL/pgSQL. ReleaseCurrentSubTransaction and exception handling, e.g. in PL/pgSQL. ReleaseCurrentSubTransaction and
RollbackAndReleaseCurrentSubTransaction allows the subsystem to close said RollbackAndReleaseCurrentSubTransaction allows the subsystem to close said
subtransactions. The main difference between this and the savepoint/release subtransactions. The main difference between this and the savepoint/release
path is that BeginInternalSubtransaction is allowed when no explicit path is that we execute the complete state transition immediately in each
transaction block has been established, while DefineSavepoint is not. subroutine, rather than deferring some work until CommitTransactionCommand.
Another difference is that BeginInternalSubtransaction is allowed when no
explicit transaction block has been established, while DefineSavepoint is not.
Subtransaction numbering
------------------------
A top-level transaction is always given a TransactionId (XID) as soon as it is
created. This is necessary for a number of reasons, notably XMIN bookkeeping
for VACUUM. However, a subtransaction doesn't need its own XID unless it
(or one of its child subxacts) writes tuples into the database. Therefore,
we postpone assigning XIDs to subxacts until and unless they call
GetCurrentTransactionId. The subsidiary actions of obtaining a lock on the
XID and and entering it into pg_subtrans and PG_PROC are done at the same time.
Internally, a backend needs a way to identify subtransactions whether or not
they have XIDs; but this need only lasts as long as the parent top transaction
endures. Therefore, we have SubTransactionId, which is somewhat like
CommandId in that it's generated from a counter that we reset at the start of
each top transaction. The top-level transaction itself has SubTransactionId 1,
and subtransactions have IDs 2 and up. (Zero is reserved for
InvalidSubTransactionId.)
pg_clog and pg_subtrans pg_clog and pg_subtrans
...@@ -197,27 +217,28 @@ there's a long running transaction or a backend sitting idle with an open ...@@ -197,27 +217,28 @@ there's a long running transaction or a backend sitting idle with an open
transaction, it may be necessary to be able to read and write this information transaction, it may be necessary to be able to read and write this information
from disk. They also allow information to be permanent across server restarts. from disk. They also allow information to be permanent across server restarts.
pg_clog records the commit status for each transaction. A transaction can be pg_clog records the commit status for each transaction that has been assigned
in progress, committed, aborted, or "sub-committed". This last state means an XID. A transaction can be in progress, committed, aborted, or
that it's a subtransaction that's no longer running, but its parent has not "sub-committed". This last state means that it's a subtransaction that's no
updated its state yet (either it is still running, or the backend crashed longer running, but its parent has not updated its state yet (either it is
without updating its status). A sub-committed transaction's status will be still running, or the backend crashed without updating its status). A
updated again to the final value as soon as the parent commits or aborts, or sub-committed transaction's status will be updated again to the final value as
when the parent is detected to be aborted. soon as the parent commits or aborts, or when the parent is detected to be
aborted.
Savepoints are implemented using subtransactions. A subtransaction is a Savepoints are implemented using subtransactions. A subtransaction is a
transaction inside a transaction; it gets its own TransactionId, but its transaction inside a transaction; its commit or abort status is not only
commit or abort status is not only dependent on whether it committed itself, dependent on whether it committed itself, but also whether its parent
but also whether its parent transaction committed. To implement multiple transaction committed. To implement multiple savepoints in a transaction we
savepoints in a transaction we allow unlimited transaction nesting depth, so allow unlimited transaction nesting depth, so any particular subtransaction's
any particular subtransaction's commit state is dependent on the commit status commit state is dependent on the commit status of each and every ancestor
of each and every ancestor transaction. transaction.
The "subtransaction parent" (pg_subtrans) mechanism records, for each The "subtransaction parent" (pg_subtrans) mechanism records, for each
transaction, the TransactionId of its parent transaction. This information is transaction with an XID, the TransactionId of its parent transaction. This
stored as soon as the subtransaction is created. Top-level transactions do information is stored as soon as the subtransaction is assigned an XID.
not have a parent, so they leave their pg_subtrans entries set to the default Top-level transactions do not have a parent, so they leave their pg_subtrans
value of zero (InvalidTransactionId). entries set to the default value of zero (InvalidTransactionId).
pg_subtrans is used to check whether the transaction in question is still pg_subtrans is used to check whether the transaction in question is still
running --- the main Xid of a transaction is recorded in the PGPROC struct, running --- the main Xid of a transaction is recorded in the PGPROC struct,
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.188 2004/09/13 20:06:04 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.189 2004/09/16 16:58:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,6 +45,19 @@ ...@@ -45,6 +45,19 @@
#include "pgstat.h" #include "pgstat.h"
/*
* User-tweakable parameters
*/
int DefaultXactIsoLevel = XACT_READ_COMMITTED;
int XactIsoLevel;
bool DefaultXactReadOnly = false;
bool XactReadOnly;
int CommitDelay = 0; /* precommit delay in microseconds */
int CommitSiblings = 5; /* # concurrent xacts needed to sleep */
/* /*
* transaction states - transaction state from server perspective * transaction states - transaction state from server perspective
*/ */
...@@ -59,29 +72,33 @@ typedef enum TransState ...@@ -59,29 +72,33 @@ typedef enum TransState
/* /*
* transaction block states - transaction state of client queries * transaction block states - transaction state of client queries
*
* Note: the subtransaction states are used only for non-topmost
* transactions; the others appear only in the topmost transaction.
*/ */
typedef enum TBlockState typedef enum TBlockState
{ {
/* not-in-transaction-block states */ /* not-in-transaction-block states */
TBLOCK_DEFAULT, TBLOCK_DEFAULT, /* idle */
TBLOCK_STARTED, TBLOCK_STARTED, /* running single-query transaction */
/* transaction block states */ /* transaction block states */
TBLOCK_BEGIN, TBLOCK_BEGIN, /* starting transaction block */
TBLOCK_INPROGRESS, TBLOCK_INPROGRESS, /* live transaction */
TBLOCK_END, TBLOCK_END, /* COMMIT received */
TBLOCK_ABORT, TBLOCK_ABORT, /* failed xact, awaiting ROLLBACK */
TBLOCK_ENDABORT, TBLOCK_ABORT_END, /* failed xact, ROLLBACK received */
TBLOCK_ABORT_PENDING, /* live xact, ROLLBACK received */
/* subtransaction states */ /* subtransaction states */
TBLOCK_SUBBEGIN, TBLOCK_SUBBEGIN, /* starting a subtransaction */
TBLOCK_SUBINPROGRESS, TBLOCK_SUBINPROGRESS, /* live subtransaction */
TBLOCK_SUBEND, TBLOCK_SUBEND, /* RELEASE received */
TBLOCK_SUBABORT, TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */
TBLOCK_SUBABORT_PENDING, TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
TBLOCK_SUBENDABORT_ALL, TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
TBLOCK_SUBENDABORT_RELEASE, TBLOCK_SUBRESTART, /* live subxact, ROLLBACK TO received */
TBLOCK_SUBENDABORT TBLOCK_SUBABORT_RESTART /* failed subxact, ROLLBACK TO received */
} TBlockState; } TBlockState;
/* /*
...@@ -89,10 +106,10 @@ typedef enum TBlockState ...@@ -89,10 +106,10 @@ typedef enum TBlockState
*/ */
typedef struct TransactionStateData typedef struct TransactionStateData
{ {
TransactionId transactionIdData; /* my XID */ TransactionId transactionId; /* my XID, or Invalid if none */
SubTransactionId subTransactionId; /* my subxact ID */
char *name; /* savepoint name, if any */ char *name; /* savepoint name, if any */
int savepointLevel; /* savepoint level */ int savepointLevel; /* savepoint level */
CommandId commandId; /* current CID */
TransState state; /* low-level state */ TransState state; /* low-level state */
TBlockState blockState; /* high-level state */ TBlockState blockState; /* high-level state */
int nestingLevel; /* nest depth */ int nestingLevel; /* nest depth */
...@@ -115,42 +132,6 @@ typedef TransactionStateData *TransactionState; ...@@ -115,42 +132,6 @@ typedef TransactionStateData *TransactionState;
#define lfirst_xid(lc) ((TransactionId) lfirst_int(lc)) #define lfirst_xid(lc) ((TransactionId) lfirst_int(lc))
#define lappend_xid(list, datum) lappend_int(list, (int) (datum)) #define lappend_xid(list, datum) lappend_int(list, (int) (datum))
static void AbortTransaction(void);
static void AtAbort_Memory(void);
static void AtCleanup_Memory(void);
static void AtCommit_LocalCache(void);
static void AtCommit_Memory(void);
static void AtStart_Cache(void);
static void AtStart_Memory(void);
static void AtStart_ResourceOwner(void);
static void CallXactCallbacks(XactEvent event, TransactionId parentXid);
static void CleanupTransaction(void);
static void CommitTransaction(void);
static void RecordTransactionAbort(void);
static void StartTransaction(void);
static void RecordSubTransactionCommit(void);
static void StartSubTransaction(void);
static void CommitSubTransaction(void);
static void AbortSubTransaction(void);
static void CleanupSubTransaction(void);
static void StartAbortedSubTransaction(void);
static void PushTransaction(void);
static void PopTransaction(void);
static char *CleanupAbortedSubTransactions(bool returnName);
static void AtSubAbort_Memory(void);
static void AtSubCleanup_Memory(void);
static void AtSubCommit_Memory(void);
static void AtSubStart_Memory(void);
static void AtSubStart_ResourceOwner(void);
static void ShowTransactionState(const char *str);
static void ShowTransactionStateRec(TransactionState state);
static const char *BlockStateAsString(TBlockState blockState);
static const char *TransStateAsString(TransState state);
/* /*
* CurrentTransactionState always points to the current transaction state * CurrentTransactionState always points to the current transaction state
* block. It will point to TopTransactionStateData when not in a * block. It will point to TopTransactionStateData when not in a
...@@ -158,9 +139,9 @@ static const char *TransStateAsString(TransState state); ...@@ -158,9 +139,9 @@ static const char *TransStateAsString(TransState state);
*/ */
static TransactionStateData TopTransactionStateData = { static TransactionStateData TopTransactionStateData = {
0, /* transaction id */ 0, /* transaction id */
0, /* subtransaction id */
NULL, /* savepoint name */ NULL, /* savepoint name */
0, /* savepoint level */ 0, /* savepoint level */
FirstCommandId, /* command id */
TRANS_DEFAULT, /* transaction state */ TRANS_DEFAULT, /* transaction state */
TBLOCK_DEFAULT, /* transaction block state from the client TBLOCK_DEFAULT, /* transaction block state from the client
* perspective */ * perspective */
...@@ -175,6 +156,13 @@ static TransactionStateData TopTransactionStateData = { ...@@ -175,6 +156,13 @@ static TransactionStateData TopTransactionStateData = {
static TransactionState CurrentTransactionState = &TopTransactionStateData; static TransactionState CurrentTransactionState = &TopTransactionStateData;
/*
* The subtransaction ID and command ID assignment counters are global
* to a whole transaction, so we do not keep them in the state stack.
*/
static SubTransactionId currentSubTransactionId;
static CommandId currentCommandId;
/* /*
* These vars hold the value of now(), ie, the transaction start time. * These vars hold the value of now(), ie, the transaction start time.
* This does not change as we enter and exit subtransactions, so we don't * This does not change as we enter and exit subtransactions, so we don't
...@@ -184,20 +172,6 @@ static AbsoluteTime xactStartTime; /* integer part */ ...@@ -184,20 +172,6 @@ static AbsoluteTime xactStartTime; /* integer part */
static int xactStartTimeUsec; /* microsecond part */ static int xactStartTimeUsec; /* microsecond part */
/*
* User-tweakable parameters
*/
int DefaultXactIsoLevel = XACT_READ_COMMITTED;
int XactIsoLevel;
bool DefaultXactReadOnly = false;
bool XactReadOnly;
int CommitDelay = 0; /* precommit delay in microseconds */
int CommitSiblings = 5; /* number of concurrent xacts needed to
* sleep */
/* /*
* List of add-on start- and end-of-xact callbacks * List of add-on start- and end-of-xact callbacks
*/ */
...@@ -210,10 +184,61 @@ typedef struct XactCallbackItem ...@@ -210,10 +184,61 @@ typedef struct XactCallbackItem
static XactCallbackItem *Xact_callbacks = NULL; static XactCallbackItem *Xact_callbacks = NULL;
/*
* List of add-on start- and end-of-subxact callbacks
*/
typedef struct SubXactCallbackItem
{
struct SubXactCallbackItem *next;
SubXactCallback callback;
void *arg;
} SubXactCallbackItem;
static SubXactCallbackItem *SubXact_callbacks = NULL;
static void (*_RollbackFunc) (void *) = NULL; static void (*_RollbackFunc) (void *) = NULL;
static void *_RollbackData = NULL; static void *_RollbackData = NULL;
/* local function prototypes */
static void AssignSubTransactionId(TransactionState s);
static void AbortTransaction(void);
static void AtAbort_Memory(void);
static void AtCleanup_Memory(void);
static void AtCommit_LocalCache(void);
static void AtCommit_Memory(void);
static void AtStart_Cache(void);
static void AtStart_Memory(void);
static void AtStart_ResourceOwner(void);
static void CallXactCallbacks(XactEvent event);
static void CallSubXactCallbacks(SubXactEvent event,
SubTransactionId mySubid,
SubTransactionId parentSubid);
static void CleanupTransaction(void);
static void CommitTransaction(void);
static void RecordTransactionAbort(void);
static void StartTransaction(void);
static void RecordSubTransactionCommit(void);
static void StartSubTransaction(void);
static void CommitSubTransaction(void);
static void AbortSubTransaction(void);
static void CleanupSubTransaction(void);
static void PushTransaction(void);
static void PopTransaction(void);
static void AtSubAbort_Memory(void);
static void AtSubCleanup_Memory(void);
static void AtSubCommit_Memory(void);
static void AtSubStart_Memory(void);
static void AtSubStart_ResourceOwner(void);
static void ShowTransactionState(const char *str);
static void ShowTransactionStateRec(TransactionState state);
static const char *BlockStateAsString(TBlockState blockState);
static const char *TransStateAsString(TransState state);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* transaction state accessors * transaction state accessors
* ---------------------------------------------------------------- * ----------------------------------------------------------------
...@@ -278,19 +303,99 @@ IsAbortedTransactionBlockState(void) ...@@ -278,19 +303,99 @@ IsAbortedTransactionBlockState(void)
TransactionId TransactionId
GetTopTransactionId(void) GetTopTransactionId(void)
{ {
return TopTransactionStateData.transactionIdData; return TopTransactionStateData.transactionId;
} }
/* /*
* GetCurrentTransactionId * GetCurrentTransactionId
*
* We do not assign XIDs to subtransactions until/unless this is called.
* When we do assign an XID to a subtransaction, recursively make sure
* its parent has one as well (this maintains the invariant that a child
* transaction has an XID following its parent's).
*/ */
TransactionId TransactionId
GetCurrentTransactionId(void) GetCurrentTransactionId(void)
{ {
TransactionState s = CurrentTransactionState; TransactionState s = CurrentTransactionState;
return s->transactionIdData; if (!TransactionIdIsValid(s->transactionId))
AssignSubTransactionId(s);
return s->transactionId;
}
static void
AssignSubTransactionId(TransactionState s)
{
ResourceOwner currentOwner;
Assert(s->parent != NULL);
Assert(s->state == TRANS_INPROGRESS);
if (!TransactionIdIsValid(s->parent->transactionId))
AssignSubTransactionId(s->parent);
/*
* Generate a new Xid and record it in PG_PROC and pg_subtrans.
*
* NB: we must make the subtrans entry BEFORE the Xid appears anywhere
* in shared storage other than PG_PROC; because if there's no room for
* it in PG_PROC, the subtrans entry is needed to ensure that other
* backends see the Xid as "running". See GetNewTransactionId.
*/
s->transactionId = GetNewTransactionId(true);
SubTransSetParent(s->transactionId, s->parent->transactionId);
/*
* Acquire lock on the transaction XID. (We assume this cannot block.)
* We have to be sure that the lock is assigned to the transaction's
* ResourceOwner.
*/
currentOwner = CurrentResourceOwner;
PG_TRY();
{
CurrentResourceOwner = s->curTransactionOwner;
XactLockTableInsert(s->transactionId);
}
PG_CATCH();
{
/* Ensure CurrentResourceOwner is restored on error */
CurrentResourceOwner = currentOwner;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = currentOwner;
}
/*
* GetCurrentTransactionIdIfAny
*
* Unlike GetCurrentTransactionId, this will return InvalidTransactionId
* if we are currently not in a transaction, or in a transaction or
* subtransaction that has not yet assigned itself an XID.
*/
TransactionId
GetCurrentTransactionIdIfAny(void)
{
TransactionState s = CurrentTransactionState;
return s->transactionId;
}
/*
* GetCurrentSubTransactionId
*/
SubTransactionId
GetCurrentSubTransactionId(void)
{
TransactionState s = CurrentTransactionState;
return s->subTransactionId;
} }
...@@ -300,9 +405,8 @@ GetCurrentTransactionId(void) ...@@ -300,9 +405,8 @@ GetCurrentTransactionId(void)
CommandId CommandId
GetCurrentCommandId(void) GetCurrentCommandId(void)
{ {
TransactionState s = CurrentTransactionState; /* this is global to a transaction, not subtransaction-local */
return currentCommandId;
return s->commandId;
} }
...@@ -374,7 +478,9 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) ...@@ -374,7 +478,9 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
if (s->state == TRANS_ABORT) if (s->state == TRANS_ABORT)
continue; continue;
if (TransactionIdEquals(xid, s->transactionIdData)) if (!TransactionIdIsValid(s->transactionId))
continue; /* it can't have any child XIDs either */
if (TransactionIdEquals(xid, s->transactionId))
return true; return true;
foreach(cell, s->childXids) foreach(cell, s->childXids)
{ {
...@@ -393,19 +499,20 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) ...@@ -393,19 +499,20 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
void void
CommandCounterIncrement(void) CommandCounterIncrement(void)
{ {
TransactionState s = CurrentTransactionState; currentCommandId += 1;
if (currentCommandId == FirstCommandId) /* check for overflow */
s->commandId += 1; {
if (s->commandId == FirstCommandId) /* check for overflow */ currentCommandId -= 1;
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("cannot have more than 2^32-1 commands in a transaction"))); errmsg("cannot have more than 2^32-1 commands in a transaction")));
}
/* Propagate new command ID into static snapshots, if set */ /* Propagate new command ID into static snapshots, if set */
if (SerializableSnapshot) if (SerializableSnapshot)
SerializableSnapshot->curcid = s->commandId; SerializableSnapshot->curcid = currentCommandId;
if (LatestSnapshot) if (LatestSnapshot)
LatestSnapshot->curcid = s->commandId; LatestSnapshot->curcid = currentCommandId;
/* /*
* make cache changes visible to me. * make cache changes visible to me.
...@@ -786,7 +893,7 @@ AtSubCommit_childXids(void) ...@@ -786,7 +893,7 @@ AtSubCommit_childXids(void)
old_cxt = MemoryContextSwitchTo(s->parent->curTransactionContext); old_cxt = MemoryContextSwitchTo(s->parent->curTransactionContext);
s->parent->childXids = lappend_xid(s->parent->childXids, s->parent->childXids = lappend_xid(s->parent->childXids,
s->transactionIdData); s->transactionId);
s->parent->childXids = list_concat(s->parent->childXids, s->childXids); s->parent->childXids = list_concat(s->parent->childXids, s->childXids);
s->childXids = NIL; /* ensure list not doubly referenced */ s->childXids = NIL; /* ensure list not doubly referenced */
...@@ -1063,7 +1170,8 @@ RecordSubTransactionAbort(void) ...@@ -1063,7 +1170,8 @@ RecordSubTransactionAbort(void)
/* /*
* Mark the transaction aborted in clog. This is not absolutely * Mark the transaction aborted in clog. This is not absolutely
* necessary but we may as well do it while we are here. * necessary but XactLockTableWait makes use of it to avoid waiting
* for already-aborted subtransactions.
*/ */
TransactionIdAbort(xid); TransactionIdAbort(xid);
TransactionIdAbortTree(nchildren, children); TransactionIdAbortTree(nchildren, children);
...@@ -1142,7 +1250,9 @@ AtSubCleanup_Memory(void) ...@@ -1142,7 +1250,9 @@ AtSubCleanup_Memory(void)
* can go too (note this also kills CurTransactionContexts from any * can go too (note this also kills CurTransactionContexts from any
* children of the subxact). * children of the subxact).
*/ */
if (s->curTransactionContext)
MemoryContextDelete(s->curTransactionContext); MemoryContextDelete(s->curTransactionContext);
s->curTransactionContext = NULL;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -1156,7 +1266,13 @@ AtSubCleanup_Memory(void) ...@@ -1156,7 +1266,13 @@ AtSubCleanup_Memory(void)
static void static void
StartTransaction(void) StartTransaction(void)
{ {
TransactionState s = CurrentTransactionState; TransactionState s;
/*
* Let's just make sure the state stack is empty
*/
s = &TopTransactionStateData;
CurrentTransactionState = s;
/* /*
* check the current transaction state * check the current transaction state
...@@ -1179,6 +1295,13 @@ StartTransaction(void) ...@@ -1179,6 +1295,13 @@ StartTransaction(void)
XactIsoLevel = DefaultXactIsoLevel; XactIsoLevel = DefaultXactIsoLevel;
XactReadOnly = DefaultXactReadOnly; XactReadOnly = DefaultXactReadOnly;
/*
* reinitialize within-transaction counters
*/
s->subTransactionId = TopSubTransactionId;
currentSubTransactionId = TopSubTransactionId;
currentCommandId = FirstCommandId;
/* /*
* must initialize resource-management stuff first * must initialize resource-management stuff first
*/ */
...@@ -1188,9 +1311,9 @@ StartTransaction(void) ...@@ -1188,9 +1311,9 @@ StartTransaction(void)
/* /*
* generate a new transaction id * generate a new transaction id
*/ */
s->transactionIdData = GetNewTransactionId(false); s->transactionId = GetNewTransactionId(false);
XactLockTableInsert(s->transactionIdData); XactLockTableInsert(s->transactionId);
/* /*
* set now() * set now()
...@@ -1200,7 +1323,6 @@ StartTransaction(void) ...@@ -1200,7 +1323,6 @@ StartTransaction(void)
/* /*
* initialize current transaction state fields * initialize current transaction state fields
*/ */
s->commandId = FirstCommandId;
s->nestingLevel = 1; s->nestingLevel = 1;
s->childXids = NIL; s->childXids = NIL;
...@@ -1332,7 +1454,7 @@ CommitTransaction(void) ...@@ -1332,7 +1454,7 @@ CommitTransaction(void)
* backend-wide state. * backend-wide state.
*/ */
CallXactCallbacks(XACT_EVENT_COMMIT, InvalidTransactionId); CallXactCallbacks(XACT_EVENT_COMMIT);
ResourceOwnerRelease(TopTransactionResourceOwner, ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS,
...@@ -1365,7 +1487,7 @@ CommitTransaction(void) ...@@ -1365,7 +1487,7 @@ CommitTransaction(void)
AtEOXact_GUC(true, false); AtEOXact_GUC(true, false);
AtEOXact_SPI(true); AtEOXact_SPI(true);
AtEOXact_on_commit_actions(true, s->transactionIdData); AtEOXact_on_commit_actions(true);
AtEOXact_Namespace(true); AtEOXact_Namespace(true);
/* smgrcommit already done */ /* smgrcommit already done */
AtEOXact_Files(); AtEOXact_Files();
...@@ -1379,6 +1501,8 @@ CommitTransaction(void) ...@@ -1379,6 +1501,8 @@ CommitTransaction(void)
AtCommit_Memory(); AtCommit_Memory();
s->transactionId = InvalidTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0; s->nestingLevel = 0;
s->childXids = NIL; s->childXids = NIL;
...@@ -1486,7 +1610,7 @@ AbortTransaction(void) ...@@ -1486,7 +1610,7 @@ AbortTransaction(void)
* ordering. * ordering.
*/ */
CallXactCallbacks(XACT_EVENT_ABORT, InvalidTransactionId); CallXactCallbacks(XACT_EVENT_ABORT);
ResourceOwnerRelease(TopTransactionResourceOwner, ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS,
...@@ -1502,7 +1626,7 @@ AbortTransaction(void) ...@@ -1502,7 +1626,7 @@ AbortTransaction(void)
AtEOXact_GUC(false, false); AtEOXact_GUC(false, false);
AtEOXact_SPI(false); AtEOXact_SPI(false);
AtEOXact_on_commit_actions(false, s->transactionIdData); AtEOXact_on_commit_actions(false);
AtEOXact_Namespace(false); AtEOXact_Namespace(false);
smgrabort(); smgrabort();
AtEOXact_Files(); AtEOXact_Files();
...@@ -1535,6 +1659,7 @@ CleanupTransaction(void) ...@@ -1535,6 +1659,7 @@ CleanupTransaction(void)
AtCleanup_Portals(); /* now safe to release portal memory */ AtCleanup_Portals(); /* now safe to release portal memory */
CurrentResourceOwner = NULL; /* and resource owner */ CurrentResourceOwner = NULL; /* and resource owner */
if (TopTransactionResourceOwner)
ResourceOwnerDelete(TopTransactionResourceOwner); ResourceOwnerDelete(TopTransactionResourceOwner);
s->curTransactionOwner = NULL; s->curTransactionOwner = NULL;
CurTransactionResourceOwner = NULL; CurTransactionResourceOwner = NULL;
...@@ -1542,6 +1667,8 @@ CleanupTransaction(void) ...@@ -1542,6 +1667,8 @@ CleanupTransaction(void)
AtCleanup_Memory(); /* and transaction memory */ AtCleanup_Memory(); /* and transaction memory */
s->transactionId = InvalidTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0; s->nestingLevel = 0;
s->childXids = NIL; s->childXids = NIL;
...@@ -1572,20 +1699,23 @@ StartTransactionCommand(void) ...@@ -1572,20 +1699,23 @@ StartTransactionCommand(void)
break; break;
/* /*
* This is the case when we are somewhere in a transaction * We are somewhere in a transaction block or subtransaction
* block and about to start a new command. For now we do * and about to start a new command. For now we do nothing,
* nothing but someday we may do command-local resource * but someday we may do command-local resource initialization.
* initialization. * (Note that any needed CommandCounterIncrement was done by
* the previous CommitTransactionCommand.)
*/ */
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
break; break;
/* /*
* Here we are in the middle of a transaction block but one of * Here we are in a failed transaction block (one of
* the commands caused an abort so we do nothing but remain in * the commands caused an abort) so we do nothing but remain in
* the abort state. Eventually we will get to the "END * the abort state. Eventually we will get a ROLLBACK command
* TRANSACTION" which will set things straight. * which will get us out of this state. (It is up to other
* code to ensure that no commands other than ROLLBACK will be
* processed in these states.)
*/ */
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
...@@ -1597,12 +1727,13 @@ StartTransactionCommand(void) ...@@ -1597,12 +1727,13 @@ StartTransactionCommand(void)
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_ENDABORT: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "StartTransactionCommand: unexpected state %s", elog(ERROR, "StartTransactionCommand: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
} }
...@@ -1628,18 +1759,16 @@ CommitTransactionCommand(void) ...@@ -1628,18 +1759,16 @@ CommitTransactionCommand(void)
/* /*
* This shouldn't happen, because it means the previous * This shouldn't happen, because it means the previous
* StartTransactionCommand didn't set the STARTED state * StartTransactionCommand didn't set the STARTED state
* appropriately, or we didn't manage previous pending abort * appropriately.
* states.
*/ */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_SUBABORT_PENDING:
elog(FATAL, "CommitTransactionCommand: unexpected state %s", elog(FATAL, "CommitTransactionCommand: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
/* /*
* If we aren't in a transaction block, just do our usual * If we aren't in a transaction block, just do our usual
* transaction commit. * transaction commit, and return to the idle state.
*/ */
case TBLOCK_STARTED: case TBLOCK_STARTED:
CommitTransaction(); CommitTransaction();
...@@ -1647,10 +1776,10 @@ CommitTransactionCommand(void) ...@@ -1647,10 +1776,10 @@ CommitTransactionCommand(void)
break; break;
/* /*
* This is the case right after we get a "BEGIN TRANSACTION" * We are completing a "BEGIN TRANSACTION" command, so we
* command, but the user hasn't done anything else yet, so we
* change to the "transaction block in progress" state and * change to the "transaction block in progress" state and
* return. * return. (We assume the BEGIN did nothing to the database,
* so we need no CommandCounterIncrement.)
*/ */
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
s->blockState = TBLOCK_INPROGRESS; s->blockState = TBLOCK_INPROGRESS;
...@@ -1662,13 +1791,13 @@ CommitTransactionCommand(void) ...@@ -1662,13 +1791,13 @@ CommitTransactionCommand(void)
* command counter and return. * command counter and return.
*/ */
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
CommandCounterIncrement(); CommandCounterIncrement();
break; break;
/* /*
* This is the case when we just got the "END TRANSACTION" * We are completing a "COMMIT" command. Do it and return to
* statement, so we commit the transaction and go back to the * the idle state.
* default state.
*/ */
case TBLOCK_END: case TBLOCK_END:
CommitTransaction(); CommitTransaction();
...@@ -1678,32 +1807,31 @@ CommitTransactionCommand(void) ...@@ -1678,32 +1807,31 @@ CommitTransactionCommand(void)
/* /*
* Here we are in the middle of a transaction block but one of * Here we are in the middle of a transaction block but one of
* the commands caused an abort so we do nothing but remain in * the commands caused an abort so we do nothing but remain in
* the abort state. Eventually we will get to the "END * the abort state. Eventually we will get a ROLLBACK comand.
* TRANSACTION" which will set things straight.
*/ */
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
break; break;
/* /*
* Here we were in an aborted transaction block which just * Here we were in an aborted transaction block and we just
* processed the "END TRANSACTION" command from the user, so * got the ROLLBACK command from the user, so clean up the
* clean up and return to the default state. * already-aborted transaction and return to the idle state.
*/ */
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
break; break;
/* /*
* Ditto, but in a subtransaction. AbortOutOfAnyTransaction * Here we were in a perfectly good transaction block but the
* will do the dirty work. * user told us to ROLLBACK anyway. We have to abort the
* transaction and then clean up.
*/ */
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_PENDING:
AbortOutOfAnyTransaction(); AbortTransaction();
s = CurrentTransactionState; /* changed by CleanupTransaction();
* AbortOutOfAnyTransaction s->blockState = TBLOCK_DEFAULT;
* */
/* AbortOutOfAnyTransaction sets the blockState */
break; break;
/* /*
...@@ -1717,24 +1845,16 @@ CommitTransactionCommand(void) ...@@ -1717,24 +1845,16 @@ CommitTransactionCommand(void)
s->blockState = TBLOCK_SUBINPROGRESS; s->blockState = TBLOCK_SUBINPROGRESS;
break; break;
/*
* Inside a subtransaction, increment the command counter.
*/
case TBLOCK_SUBINPROGRESS:
CommandCounterIncrement();
break;
/* /*
* We were issued a COMMIT or RELEASE command, so we end the * We were issued a COMMIT or RELEASE command, so we end the
* current subtransaction and return to the parent transaction. * current subtransaction and return to the parent transaction.
* Lather, rinse, and repeat until we get out of all SUBEND'ed * The parent might be ended too, so repeat till we are all the
* subtransaction levels. * way out or find an INPROGRESS transaction.
*/ */
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
do do
{ {
CommitSubTransaction(); CommitSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
} while (s->blockState == TBLOCK_SUBEND); } while (s->blockState == TBLOCK_SUBEND);
/* If we had a COMMIT command, finish off the main xact too */ /* If we had a COMMIT command, finish off the main xact too */
...@@ -1744,36 +1864,54 @@ CommitTransactionCommand(void) ...@@ -1744,36 +1864,54 @@ CommitTransactionCommand(void)
CommitTransaction(); CommitTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
} }
else
{
Assert(s->blockState == TBLOCK_INPROGRESS ||
s->blockState == TBLOCK_SUBINPROGRESS);
}
break; break;
/* /*
* If we are in an aborted subtransaction, do nothing. * The current already-failed subtransaction is ending due to a
* ROLLBACK or ROLLBACK TO command, so pop it and recursively
* examine the parent (which could be in any of several states).
*/ */
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT_END:
CleanupSubTransaction();
CommitTransactionCommand();
break; break;
/* /*
* The current subtransaction is ending. Do the equivalent of * As above, but it's not dead yet, so abort first.
* a ROLLBACK TO followed by a RELEASE command.
*/ */
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBABORT_PENDING:
CleanupAbortedSubTransactions(false); AbortSubTransaction();
CleanupSubTransaction();
CommitTransactionCommand();
break; break;
/* /*
* The current subtransaction is ending due to a ROLLBACK TO * The current subtransaction is the target of a ROLLBACK TO
* command, so close all savepoints up to the target level. * command. Abort and pop it, then start a new subtransaction
* When finished, recreate the savepoint. * with the same name.
*/ */
case TBLOCK_SUBENDABORT: case TBLOCK_SUBRESTART:
{ {
char *name = CleanupAbortedSubTransactions(true); char *name;
int savepointLevel;
Assert(PointerIsValid(name)); /* save name and keep Cleanup from freeing it */
DefineSavepoint(name); name = s->name;
s = CurrentTransactionState; /* changed by s->name = NULL;
* DefineSavepoint */ savepointLevel = s->savepointLevel;
pfree(name);
AbortSubTransaction();
CleanupSubTransaction();
DefineSavepoint(NULL);
s = CurrentTransactionState; /* changed by push */
s->name = name;
s->savepointLevel = savepointLevel;
/* This is the same as TBLOCK_SUBBEGIN case */ /* This is the same as TBLOCK_SUBBEGIN case */
AssertState(s->blockState == TBLOCK_SUBBEGIN); AssertState(s->blockState == TBLOCK_SUBBEGIN);
...@@ -1781,56 +1919,35 @@ CommitTransactionCommand(void) ...@@ -1781,56 +1919,35 @@ CommitTransactionCommand(void)
s->blockState = TBLOCK_SUBINPROGRESS; s->blockState = TBLOCK_SUBINPROGRESS;
} }
break; break;
}
}
/*
* CleanupAbortedSubTransactions
*
* Helper function for CommitTransactionCommand. Aborts and cleans up
* dead subtransactions after a ROLLBACK TO command. Optionally returns
* the name of the last dead subtransaction so it can be reused to redefine
* the savepoint. (Caller is responsible for pfree'ing the result.)
*/
static char *
CleanupAbortedSubTransactions(bool returnName)
{
TransactionState s = CurrentTransactionState;
char *name = NULL;
AssertState(PointerIsValid(s->parent));
Assert(s->parent->blockState == TBLOCK_SUBINPROGRESS ||
s->parent->blockState == TBLOCK_INPROGRESS ||
s->parent->blockState == TBLOCK_STARTED ||
s->parent->blockState == TBLOCK_SUBABORT_PENDING);
/* /*
* Abort everything up to the target level. The current * Same as above, but the subtransaction had already failed,
* subtransaction only needs cleanup. If we need to save the name, * so we don't need AbortSubTransaction.
* look for the last subtransaction in TBLOCK_SUBABORT_PENDING state.
*/ */
if (returnName && s->parent->blockState != TBLOCK_SUBABORT_PENDING) case TBLOCK_SUBABORT_RESTART:
name = MemoryContextStrdup(TopMemoryContext, s->name); {
char *name;
int savepointLevel;
CleanupSubTransaction(); /* save name and keep Cleanup from freeing it */
PopTransaction(); name = s->name;
s = CurrentTransactionState; /* changed by pop */ s->name = NULL;
savepointLevel = s->savepointLevel;
while (s->blockState == TBLOCK_SUBABORT_PENDING)
{
AbortSubTransaction();
if (returnName && s->parent->blockState != TBLOCK_SUBABORT_PENDING)
name = MemoryContextStrdup(TopMemoryContext, s->name);
CleanupSubTransaction(); CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState;
}
AssertState(s->blockState == TBLOCK_SUBINPROGRESS || DefineSavepoint(NULL);
s->blockState == TBLOCK_INPROGRESS || s = CurrentTransactionState; /* changed by push */
s->blockState == TBLOCK_STARTED); s->name = name;
s->savepointLevel = savepointLevel;
return name; /* This is the same as TBLOCK_SUBBEGIN case */
AssertState(s->blockState == TBLOCK_SUBBEGIN);
StartSubTransaction();
s->blockState = TBLOCK_SUBINPROGRESS;
}
break;
}
} }
/* /*
...@@ -1861,32 +1978,32 @@ AbortCurrentTransaction(void) ...@@ -1861,32 +1978,32 @@ AbortCurrentTransaction(void)
/* /*
* If we are in TBLOCK_BEGIN it means something screwed up * If we are in TBLOCK_BEGIN it means something screwed up
* right after reading "BEGIN TRANSACTION" so we enter the * right after reading "BEGIN TRANSACTION". We assume that
* abort state. Eventually an "END TRANSACTION" will fix * the user will interpret the error as meaning the BEGIN
* things. * failed to get him into a transaction block, so we should
* abort and return to idle state.
*/ */
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
AbortTransaction(); AbortTransaction();
s->blockState = TBLOCK_ABORT; CleanupTransaction();
/* CleanupTransaction happens when we exit TBLOCK_ENDABORT */ s->blockState = TBLOCK_DEFAULT;
break; break;
/* /*
* This is the case when we are somewhere in a transaction * We are somewhere in a transaction block and we've gotten a
* block and we've gotten a failure, so we abort the * failure, so we abort the transaction and set up the persistent
* transaction and set up the persistent ABORT state. We will * ABORT state. We will stay in ABORT until we get a ROLLBACK.
* stay in ABORT until we get an "END TRANSACTION".
*/ */
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
AbortTransaction(); AbortTransaction();
s->blockState = TBLOCK_ABORT; s->blockState = TBLOCK_ABORT;
/* CleanupTransaction happens when we exit TBLOCK_ENDABORT */ /* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
break; break;
/* /*
* Here, the system was fouled up just after the user wanted * Here, we failed while trying to COMMIT. Clean up the
* to end the transaction block so we abort the transaction * transaction and return to idle state (we do not want to
* and return to the default state. * stay in the transaction).
*/ */
case TBLOCK_END: case TBLOCK_END:
AbortTransaction(); AbortTransaction();
...@@ -1896,74 +2013,65 @@ AbortCurrentTransaction(void) ...@@ -1896,74 +2013,65 @@ AbortCurrentTransaction(void)
/* /*
* Here, we are already in an aborted transaction state and * Here, we are already in an aborted transaction state and
* are waiting for an "END TRANSACTION" to come along and lo * are waiting for a ROLLBACK, but for some reason we failed
* and behold, we abort again! So we just remain in the abort * again! So we just remain in the abort state.
* state.
*/ */
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
break; break;
/* /*
* Here we were in an aborted transaction block which just * We are in a failed transaction and we got the ROLLBACK command.
* processed the "END TRANSACTION" command but somehow aborted * We have already aborted, we just need to cleanup and go to
* again.. since we must have done the abort processing, we * idle state.
* clean up and return to the default state.
*/ */
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
break; break;
/* /*
* If we are just starting a subtransaction, put it in aborted * We are in a live transaction and we got a ROLLBACK command.
* state. * Abort, cleanup, go to idle state.
*/ */
case TBLOCK_SUBBEGIN: case TBLOCK_ABORT_PENDING:
StartAbortedSubTransaction(); AbortTransaction();
s->blockState = TBLOCK_SUBABORT; CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
break; break;
/*
* We got an error inside a subtransaction. Abort just the
* subtransaction, and go to the persistent SUBABORT state
* until we get ROLLBACK.
*/
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
AbortSubTransaction(); AbortSubTransaction();
s->blockState = TBLOCK_SUBABORT; s->blockState = TBLOCK_SUBABORT;
break; break;
/* /*
* If we are aborting an ending transaction, we have to abort * If we failed while trying to create a subtransaction, clean up
* the parent transaction too. * the broken subtransaction and abort the parent. The same
* applies if we get a failure while ending a subtransaction.
*/ */
case TBLOCK_SUBBEGIN:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
AbortSubTransaction(); AbortSubTransaction();
CleanupSubTransaction(); CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */
Assert(s->blockState != TBLOCK_SUBEND &&
s->blockState != TBLOCK_SUBENDABORT);
AbortCurrentTransaction(); AbortCurrentTransaction();
break; break;
/* /*
* Same as above, except the Abort() was already done. * Same as above, except the Abort() was already done.
*/ */
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBABORT_RESTART:
CleanupSubTransaction(); CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */
Assert(s->blockState != TBLOCK_SUBEND &&
s->blockState != TBLOCK_SUBENDABORT);
AbortCurrentTransaction(); AbortCurrentTransaction();
break; break;
/*
* We are already aborting the whole transaction tree. Do
* nothing, CommitTransactionCommand will call
* AbortOutOfAnyTransaction and set things straight.
*/
case TBLOCK_SUBENDABORT_ALL:
break;
} }
} }
...@@ -2113,9 +2221,7 @@ IsInTransactionChain(void *stmtNode) ...@@ -2113,9 +2221,7 @@ IsInTransactionChain(void *stmtNode)
* (mainly because it's easier to control the order that way, where needed). * (mainly because it's easier to control the order that way, where needed).
* *
* At transaction end, the callback occurs post-commit or post-abort, so the * At transaction end, the callback occurs post-commit or post-abort, so the
* callback functions can only do noncritical cleanup. At subtransaction * callback functions can only do noncritical cleanup.
* start, the callback is called when the subtransaction has finished
* initializing.
*/ */
void void
RegisterXactCallback(XactCallback callback, void *arg) RegisterXactCallback(XactCallback callback, void *arg)
...@@ -2152,12 +2258,69 @@ UnregisterXactCallback(XactCallback callback, void *arg) ...@@ -2152,12 +2258,69 @@ UnregisterXactCallback(XactCallback callback, void *arg)
} }
static void static void
CallXactCallbacks(XactEvent event, TransactionId parentXid) CallXactCallbacks(XactEvent event)
{ {
XactCallbackItem *item; XactCallbackItem *item;
for (item = Xact_callbacks; item; item = item->next) for (item = Xact_callbacks; item; item = item->next)
(*item->callback) (event, parentXid, item->arg); (*item->callback) (event, item->arg);
}
/*
* Register or deregister callback functions for start- and end-of-subxact
* operations.
*
* Pretty much same as above, but for subtransaction events.
*
* At subtransaction end, the callback occurs post-subcommit or post-subabort,
* so the callback functions can only do noncritical cleanup. At
* subtransaction start, the callback is called when the subtransaction has
* finished initializing.
*/
void
RegisterSubXactCallback(SubXactCallback callback, void *arg)
{
SubXactCallbackItem *item;
item = (SubXactCallbackItem *)
MemoryContextAlloc(TopMemoryContext, sizeof(SubXactCallbackItem));
item->callback = callback;
item->arg = arg;
item->next = SubXact_callbacks;
SubXact_callbacks = item;
}
void
UnregisterSubXactCallback(SubXactCallback callback, void *arg)
{
SubXactCallbackItem *item;
SubXactCallbackItem *prev;
prev = NULL;
for (item = SubXact_callbacks; item; prev = item, item = item->next)
{
if (item->callback == callback && item->arg == arg)
{
if (prev)
prev->next = item->next;
else
SubXact_callbacks = item->next;
pfree(item);
break;
}
}
}
static void
CallSubXactCallbacks(SubXactEvent event,
SubTransactionId mySubid,
SubTransactionId parentSubid)
{
SubXactCallbackItem *item;
for (item = SubXact_callbacks; item; item = item->next)
(*item->callback) (event, mySubid, parentSubid, item->arg);
} }
...@@ -2197,17 +2360,18 @@ BeginTransactionBlock(void) ...@@ -2197,17 +2360,18 @@ BeginTransactionBlock(void)
errmsg("there is already a transaction in progress"))); errmsg("there is already a transaction in progress")));
break; break;
/* These cases are invalid. Reject them altogether. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_ENDABORT:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "BeginTransactionBlock: unexpected state %s", elog(FATAL, "BeginTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2220,6 +2384,11 @@ BeginTransactionBlock(void) ...@@ -2220,6 +2384,11 @@ BeginTransactionBlock(void)
* *
* Since COMMIT may actually do a ROLLBACK, the result indicates what * Since COMMIT may actually do a ROLLBACK, the result indicates what
* happened: TRUE for COMMIT, FALSE for ROLLBACK. * happened: TRUE for COMMIT, FALSE for ROLLBACK.
*
* Note that we don't actually do anything here except change blockState.
* The real work will be done in the upcoming CommitTransactionCommand().
* We do it this way because it's not convenient to change memory context,
* resource owner, etc while executing inside a Portal.
*/ */
bool bool
EndTransactionBlock(void) EndTransactionBlock(void)
...@@ -2230,11 +2399,8 @@ EndTransactionBlock(void) ...@@ -2230,11 +2399,8 @@ EndTransactionBlock(void)
switch (s->blockState) switch (s->blockState)
{ {
/* /*
* We are in a transaction block which should commit when we * We are in a transaction block, so tell CommitTransactionCommand
* get to the upcoming CommitTransactionCommand() so we set * to COMMIT.
* the state to "END". CommitTransactionCommand() will
* recognize this and commit the transaction and return us to
* the default state.
*/ */
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
s->blockState = TBLOCK_END; s->blockState = TBLOCK_END;
...@@ -2242,14 +2408,11 @@ EndTransactionBlock(void) ...@@ -2242,14 +2408,11 @@ EndTransactionBlock(void)
break; break;
/* /*
* We are in a transaction block which aborted. Since the * We are in a failed transaction block. Tell
* AbortTransaction() was already done, we need only change to * CommitTransactionCommand it's time to exit the block.
* the special "END ABORT" state. The upcoming
* CommitTransactionCommand() will recognise this and then put
* us back in the default state.
*/ */
case TBLOCK_ABORT: case TBLOCK_ABORT:
s->blockState = TBLOCK_ENDABORT; s->blockState = TBLOCK_ABORT_END;
break; break;
/* /*
...@@ -2259,50 +2422,72 @@ EndTransactionBlock(void) ...@@ -2259,50 +2422,72 @@ EndTransactionBlock(void)
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
while (s->parent != NULL) while (s->parent != NULL)
{ {
Assert(s->blockState == TBLOCK_SUBINPROGRESS); if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBEND; s->blockState = TBLOCK_SUBEND;
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
s = s->parent; s = s->parent;
} }
Assert(s->blockState == TBLOCK_INPROGRESS); if (s->blockState == TBLOCK_INPROGRESS)
s->blockState = TBLOCK_END; s->blockState = TBLOCK_END;
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
result = true; result = true;
break; break;
/* /*
* Here we are inside an aborted subtransaction. Go to the * Here we are inside an aborted subtransaction. Treat the
* "abort the whole tree" state so that * COMMIT as ROLLBACK: set up to abort everything and exit
* CommitTransactionCommand() calls AbortOutOfAnyTransaction. * the main transaction.
*/ */
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
s->blockState = TBLOCK_SUBENDABORT_ALL; while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
else if (s->blockState == TBLOCK_SUBABORT)
s->blockState = TBLOCK_SUBABORT_END;
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
s = s->parent;
}
if (s->blockState == TBLOCK_INPROGRESS)
s->blockState = TBLOCK_ABORT_PENDING;
else if (s->blockState == TBLOCK_ABORT)
s->blockState = TBLOCK_ABORT_END;
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
break; break;
case TBLOCK_STARTED:
/* /*
* here, the user issued COMMIT when not inside a transaction. * here, the user issued COMMIT when not inside a transaction.
* Issue a WARNING and go to abort state. The upcoming call * Issue a WARNING and go to abort state. The upcoming call
* to CommitTransactionCommand() will then put us back into * to CommitTransactionCommand() will then put us back into
* the default state. * the default state.
*/ */
case TBLOCK_STARTED:
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
errmsg("there is no transaction in progress"))); errmsg("there is no transaction in progress")));
AbortTransaction(); s->blockState = TBLOCK_ABORT_PENDING;
s->blockState = TBLOCK_ENDABORT;
break; break;
/* these cases are invalid. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_ENDABORT:
case TBLOCK_END:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "EndTransactionBlock: unexpected state %s", elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2314,6 +2499,8 @@ EndTransactionBlock(void) ...@@ -2314,6 +2499,8 @@ EndTransactionBlock(void)
/* /*
* UserAbortTransactionBlock * UserAbortTransactionBlock
* This executes a ROLLBACK command. * This executes a ROLLBACK command.
*
* As above, we don't actually do anything here except change blockState.
*/ */
void void
UserAbortTransactionBlock(void) UserAbortTransactionBlock(void)
...@@ -2323,46 +2510,48 @@ UserAbortTransactionBlock(void) ...@@ -2323,46 +2510,48 @@ UserAbortTransactionBlock(void)
switch (s->blockState) switch (s->blockState)
{ {
/* /*
* We are inside a failed transaction block and we got an * We are inside a transaction block and we got a ROLLBACK
* abort command from the user. Abort processing is already * command from the user, so tell CommitTransactionCommand
* done, we just need to move to the ENDABORT state so we will * to abort and exit the transaction block.
* end up in the default state after the upcoming
* CommitTransactionCommand().
*/ */
case TBLOCK_ABORT: case TBLOCK_INPROGRESS:
s->blockState = TBLOCK_ENDABORT; s->blockState = TBLOCK_ABORT_PENDING;
break; break;
/* /*
* We are inside a failed subtransaction and we got an abort * We are inside a failed transaction block and we got a ROLLBACK
* command from the user. Abort processing is already done, * command from the user. Abort processing is already done,
* so go to the "abort all" state and CommitTransactionCommand * so CommitTransactionCommand just has to cleanup and go back
* will call AbortOutOfAnyTransaction to set things straight. * to idle state.
*/ */
case TBLOCK_SUBABORT: case TBLOCK_ABORT:
s->blockState = TBLOCK_SUBENDABORT_ALL; s->blockState = TBLOCK_ABORT_END;
break;
/*
* We are inside a transaction block and we got an abort
* command from the user, so we move to the ENDABORT state and
* do abort processing so we will end up in the default state
* after the upcoming CommitTransactionCommand().
*/
case TBLOCK_INPROGRESS:
AbortTransaction();
s->blockState = TBLOCK_ENDABORT;
break; break;
/* /*
* We are inside a subtransaction. Abort the current * We are inside a subtransaction. Mark everything
* subtransaction and go to the "abort all" state, so * up to top level as exitable.
* CommitTransactionCommand will call AbortOutOfAnyTransaction
* to set things straight.
*/ */
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
AbortSubTransaction(); case TBLOCK_SUBABORT:
s->blockState = TBLOCK_SUBENDABORT_ALL; while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
s->blockState = TBLOCK_SUBABORT_PENDING;
else if (s->blockState == TBLOCK_SUBABORT)
s->blockState = TBLOCK_SUBABORT_END;
else
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
s = s->parent;
}
if (s->blockState == TBLOCK_INPROGRESS)
s->blockState = TBLOCK_ABORT_PENDING;
else if (s->blockState == TBLOCK_ABORT)
s->blockState = TBLOCK_ABORT_END;
else
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
break; break;
/* /*
...@@ -2375,21 +2564,21 @@ UserAbortTransactionBlock(void) ...@@ -2375,21 +2564,21 @@ UserAbortTransactionBlock(void)
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
errmsg("there is no transaction in progress"))); errmsg("there is no transaction in progress")));
AbortTransaction(); s->blockState = TBLOCK_ABORT_PENDING;
s->blockState = TBLOCK_ENDABORT;
break; break;
/* These cases are invalid. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_ENDABORT:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s", elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2414,28 +2603,28 @@ DefineSavepoint(char *name) ...@@ -2414,28 +2603,28 @@ DefineSavepoint(char *name)
s = CurrentTransactionState; /* changed by push */ s = CurrentTransactionState; /* changed by push */
/* /*
* Note that we are allocating the savepoint name in the * Savepoint names, like the TransactionState block itself,
* parent transaction's CurTransactionContext, since we don't * live in TopTransactionContext.
* yet have a transaction context for the new guy.
*/ */
s->name = MemoryContextStrdup(CurTransactionContext, name); if (name)
s->blockState = TBLOCK_SUBBEGIN; s->name = MemoryContextStrdup(TopTransactionContext, name);
break; break;
/* These cases are invalid. Reject them altogether. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_STARTED: case TBLOCK_STARTED:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END:
case TBLOCK_SUBEND:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
case TBLOCK_END: case TBLOCK_SUBABORT_END:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBENDABORT:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBEND: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "DefineSavepoint: unexpected state %s", elog(FATAL, "DefineSavepoint: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2445,6 +2634,8 @@ DefineSavepoint(char *name) ...@@ -2445,6 +2634,8 @@ DefineSavepoint(char *name)
/* /*
* ReleaseSavepoint * ReleaseSavepoint
* This executes a RELEASE command. * This executes a RELEASE command.
*
* As above, we don't actually do anything here except change blockState.
*/ */
void void
ReleaseSavepoint(List *options) ReleaseSavepoint(List *options)
...@@ -2455,13 +2646,13 @@ ReleaseSavepoint(List *options) ...@@ -2455,13 +2646,13 @@ ReleaseSavepoint(List *options)
ListCell *cell; ListCell *cell;
char *name = NULL; char *name = NULL;
/*
* Check valid block state transaction status.
*/
switch (s->blockState) switch (s->blockState)
{ {
/*
* We can't rollback to a savepoint if there is no savepoint
* defined.
*/
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_ABORT:
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION), (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint"))); errmsg("no such savepoint")));
...@@ -2474,19 +2665,21 @@ ReleaseSavepoint(List *options) ...@@ -2474,19 +2665,21 @@ ReleaseSavepoint(List *options)
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
break; break;
/* these cases are invalid. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_STARTED: case TBLOCK_STARTED:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_ENDABORT:
case TBLOCK_END:
case TBLOCK_SUBABORT:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "ReleaseSavepoint: unexpected state %s", elog(FATAL, "ReleaseSavepoint: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2539,6 +2732,8 @@ ReleaseSavepoint(List *options) ...@@ -2539,6 +2732,8 @@ ReleaseSavepoint(List *options)
/* /*
* RollbackToSavepoint * RollbackToSavepoint
* This executes a ROLLBACK TO <savepoint> command. * This executes a ROLLBACK TO <savepoint> command.
*
* As above, we don't actually do anything here except change blockState.
*/ */
void void
RollbackToSavepoint(List *options) RollbackToSavepoint(List *options)
...@@ -2552,11 +2747,11 @@ RollbackToSavepoint(List *options) ...@@ -2552,11 +2747,11 @@ RollbackToSavepoint(List *options)
switch (s->blockState) switch (s->blockState)
{ {
/* /*
* We can't rollback to a savepoint if there is no saveopint * We can't rollback to a savepoint if there is no savepoint
* defined. * defined.
*/ */
case TBLOCK_ABORT:
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_ABORT:
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION), (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint"))); errmsg("no such savepoint")));
...@@ -2565,27 +2760,23 @@ RollbackToSavepoint(List *options) ...@@ -2565,27 +2760,23 @@ RollbackToSavepoint(List *options)
/* /*
* There is at least one savepoint, so proceed. * There is at least one savepoint, so proceed.
*/ */
case TBLOCK_SUBABORT:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
/*
* Have to do AbortSubTransaction, but first check if this is
* the right subtransaction
*/
break; break;
/* these cases are invalid. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_STARTED: case TBLOCK_STARTED:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_ENDABORT:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "RollbackToSavepoint: unexpected state %s", elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2618,28 +2809,35 @@ RollbackToSavepoint(List *options) ...@@ -2618,28 +2809,35 @@ RollbackToSavepoint(List *options)
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION), (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("no such savepoint"))); errmsg("no such savepoint")));
/*
* Abort the current subtransaction, if needed. We can't Cleanup the
* savepoint yet, so signal CommitTransactionCommand to do it and
* close all savepoints up to the target level.
*/
if (s->blockState == TBLOCK_SUBINPROGRESS)
AbortSubTransaction();
s->blockState = TBLOCK_SUBENDABORT;
/* /*
* Mark "abort pending" all subtransactions up to the target * Mark "abort pending" all subtransactions up to the target
* subtransaction. (Except the current subtransaction!) * subtransaction. The actual aborts will happen when control gets
* to CommitTransactionCommand.
*/ */
xact = CurrentTransactionState; xact = CurrentTransactionState;
for (;;)
while (xact != target)
{ {
if (xact == target)
break;
if (xact->blockState == TBLOCK_SUBINPROGRESS)
xact->blockState = TBLOCK_SUBABORT_PENDING;
else if (xact->blockState == TBLOCK_SUBABORT)
xact->blockState = TBLOCK_SUBABORT_END;
else
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(xact->blockState));
xact = xact->parent; xact = xact->parent;
Assert(PointerIsValid(xact)); Assert(PointerIsValid(xact));
Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
xact->blockState = TBLOCK_SUBABORT_PENDING;
} }
/* And mark the target as "restart pending" */
if (xact->blockState == TBLOCK_SUBINPROGRESS)
xact->blockState = TBLOCK_SUBRESTART;
else if (xact->blockState == TBLOCK_SUBABORT)
xact->blockState = TBLOCK_SUBABORT_RESTART;
else
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(xact->blockState));
} }
/* /*
...@@ -2649,8 +2847,6 @@ RollbackToSavepoint(List *options) ...@@ -2649,8 +2847,6 @@ RollbackToSavepoint(List *options)
* be called when not inside a BEGIN block. Also, we automatically * be called when not inside a BEGIN block. Also, we automatically
* cycle through CommitTransactionCommand/StartTransactionCommand * cycle through CommitTransactionCommand/StartTransactionCommand
* instead of expecting the caller to do it. * instead of expecting the caller to do it.
*
* Optionally, name can be NULL to create an unnamed savepoint.
*/ */
void void
BeginInternalSubTransaction(char *name) BeginInternalSubTransaction(char *name)
...@@ -2667,28 +2863,27 @@ BeginInternalSubTransaction(char *name) ...@@ -2667,28 +2863,27 @@ BeginInternalSubTransaction(char *name)
s = CurrentTransactionState; /* changed by push */ s = CurrentTransactionState; /* changed by push */
/* /*
* Note that we are allocating the savepoint name in the * Savepoint names, like the TransactionState block itself,
* parent transaction's CurTransactionContext, since we don't * live in TopTransactionContext.
* yet have a transaction context for the new guy.
*/ */
if (name) if (name)
s->name = MemoryContextStrdup(CurTransactionContext, name); s->name = MemoryContextStrdup(TopTransactionContext, name);
s->blockState = TBLOCK_SUBBEGIN;
break; break;
/* These cases are invalid. Reject them altogether. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
case TBLOCK_END:
case TBLOCK_SUBEND:
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
case TBLOCK_END: case TBLOCK_SUBABORT_END:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBENDABORT:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBEND: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "BeginInternalSubTransaction: unexpected state %s", elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2716,7 +2911,6 @@ ReleaseCurrentSubTransaction(void) ...@@ -2716,7 +2911,6 @@ ReleaseCurrentSubTransaction(void)
Assert(s->state == TRANS_INPROGRESS); Assert(s->state == TRANS_INPROGRESS);
MemoryContextSwitchTo(CurTransactionContext); MemoryContextSwitchTo(CurTransactionContext);
CommitSubTransaction(); CommitSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
Assert(s->state == TRANS_INPROGRESS); Assert(s->state == TRANS_INPROGRESS);
} }
...@@ -2736,24 +2930,25 @@ RollbackAndReleaseCurrentSubTransaction(void) ...@@ -2736,24 +2930,25 @@ RollbackAndReleaseCurrentSubTransaction(void)
switch (s->blockState) switch (s->blockState)
{ {
/* Must be in a subtransaction */ /* Must be in a subtransaction */
case TBLOCK_SUBABORT:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
break; break;
/* these cases are invalid. */ /* These cases are invalid. */
case TBLOCK_DEFAULT: case TBLOCK_DEFAULT:
case TBLOCK_STARTED: case TBLOCK_STARTED:
case TBLOCK_ABORT:
case TBLOCK_INPROGRESS:
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_INPROGRESS:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_ENDABORT:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT:
case TBLOCK_SUBENDABORT: case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBBEGIN: case TBLOCK_SUBABORT_RESTART:
elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s", elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
BlockStateAsString(s->blockState)); BlockStateAsString(s->blockState));
break; break;
...@@ -2764,10 +2959,14 @@ RollbackAndReleaseCurrentSubTransaction(void) ...@@ -2764,10 +2959,14 @@ RollbackAndReleaseCurrentSubTransaction(void)
*/ */
if (s->blockState == TBLOCK_SUBINPROGRESS) if (s->blockState == TBLOCK_SUBINPROGRESS)
AbortSubTransaction(); AbortSubTransaction();
s->blockState = TBLOCK_SUBENDABORT_RELEASE;
/* And clean it up, too */ /* And clean it up, too */
CleanupAbortedSubTransactions(false); CleanupSubTransaction();
s = CurrentTransactionState; /* changed by pop */
AssertState(s->blockState == TBLOCK_SUBINPROGRESS ||
s->blockState == TBLOCK_INPROGRESS ||
s->blockState == TBLOCK_STARTED);
} }
/* /*
...@@ -2796,46 +2995,38 @@ AbortOutOfAnyTransaction(void) ...@@ -2796,46 +2995,38 @@ AbortOutOfAnyTransaction(void)
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_END: case TBLOCK_END:
case TBLOCK_ABORT_PENDING:
/* In a transaction, so clean up */ /* In a transaction, so clean up */
AbortTransaction(); AbortTransaction();
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
break; break;
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
/* AbortTransaction already done, still need Cleanup */ /* AbortTransaction already done, still need Cleanup */
CleanupTransaction(); CleanupTransaction();
s->blockState = TBLOCK_DEFAULT; s->blockState = TBLOCK_DEFAULT;
break; break;
case TBLOCK_SUBBEGIN:
/* /*
* We didn't get as far as starting the subxact, so * In a subtransaction, so clean it up and abort parent
* there's nothing to abort. Just pop back to parent. * too
*/ */
PopTransaction(); case TBLOCK_SUBBEGIN:
s = CurrentTransactionState; /* changed by pop */
break;
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
/*
* In a subtransaction, so clean it up and abort parent
* too
*/
AbortSubTransaction(); AbortSubTransaction();
CleanupSubTransaction(); CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
break; break;
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_SUBABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_RESTART:
case TBLOCK_SUBENDABORT_RELEASE:
/* As above, but AbortSubTransaction already done */ /* As above, but AbortSubTransaction already done */
CleanupSubTransaction(); CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */ s = CurrentTransactionState; /* changed by pop */
break; break;
} }
...@@ -2891,19 +3082,20 @@ TransactionBlockStatusCode(void) ...@@ -2891,19 +3082,20 @@ TransactionBlockStatusCode(void)
case TBLOCK_STARTED: case TBLOCK_STARTED:
return 'I'; /* idle --- not in transaction */ return 'I'; /* idle --- not in transaction */
case TBLOCK_BEGIN: case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_INPROGRESS: case TBLOCK_INPROGRESS:
case TBLOCK_END:
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBBEGIN: case TBLOCK_END:
case TBLOCK_SUBEND: case TBLOCK_SUBEND:
return 'T'; /* in transaction */ return 'T'; /* in transaction */
case TBLOCK_ABORT: case TBLOCK_ABORT:
case TBLOCK_ENDABORT:
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_ABORT_END:
case TBLOCK_SUBENDABORT: case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
case TBLOCK_SUBABORT_RESTART:
return 'E'; /* in failed transaction */ return 'E'; /* in failed transaction */
} }
...@@ -2929,6 +3121,15 @@ IsSubTransaction(void) ...@@ -2929,6 +3121,15 @@ IsSubTransaction(void)
/* /*
* StartSubTransaction * StartSubTransaction
*
* If you're wondering why this is separate from PushTransaction: it's because
* we can't conveniently do this stuff right inside DefineSavepoint. The
* SAVEPOINT utility command will be executed inside a Portal, and if we
* muck with CurrentMemoryContext or CurrentResourceOwner then exit from
* the Portal will undo those settings. So we make DefineSavepoint just
* push a dummy transaction block, and when control returns to the main
* idle loop, CommitTransactionCommand will be called, and we'll come here
* to finish starting the subtransaction.
*/ */
static void static void
StartSubTransaction(void) StartSubTransaction(void)
...@@ -2942,33 +3143,12 @@ StartSubTransaction(void) ...@@ -2942,33 +3143,12 @@ StartSubTransaction(void)
s->state = TRANS_START; s->state = TRANS_START;
/* /*
* Initialize subsystems for new subtransaction
*
* must initialize resource-management stuff first * must initialize resource-management stuff first
*/ */
AtSubStart_Memory(); AtSubStart_Memory();
AtSubStart_ResourceOwner(); AtSubStart_ResourceOwner();
/*
* Generate a new Xid and record it in pg_subtrans. NB: we must make
* the subtrans entry BEFORE the Xid appears anywhere in shared
* storage, such as in the lock table; because until it's made the Xid
* may not appear to be "running" to other backends. See
* GetNewTransactionId.
*/
s->transactionIdData = GetNewTransactionId(true);
SubTransSetParent(s->transactionIdData, s->parent->transactionIdData);
XactLockTableInsert(s->transactionIdData);
/*
* Finish setup of other transaction state fields.
*/
s->currentUser = GetUserId();
s->prevXactReadOnly = XactReadOnly;
/*
* Initialize other subsystems for new subtransaction
*/
AtSubStart_Inval(); AtSubStart_Inval();
AtSubStart_Notify(); AtSubStart_Notify();
AfterTriggerBeginSubXact(); AfterTriggerBeginSubXact();
...@@ -2978,13 +3158,17 @@ StartSubTransaction(void) ...@@ -2978,13 +3158,17 @@ StartSubTransaction(void)
/* /*
* Call start-of-subxact callbacks * Call start-of-subxact callbacks
*/ */
CallXactCallbacks(XACT_EVENT_START_SUB, s->parent->transactionIdData); CallSubXactCallbacks(SUBXACT_EVENT_START_SUB, s->subTransactionId,
s->parent->subTransactionId);
ShowTransactionState("StartSubTransaction"); ShowTransactionState("StartSubTransaction");
} }
/* /*
* CommitSubTransaction * CommitSubTransaction
*
* The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function.
*/ */
static void static void
CommitSubTransaction(void) CommitSubTransaction(void)
...@@ -3005,28 +3189,42 @@ CommitSubTransaction(void) ...@@ -3005,28 +3189,42 @@ CommitSubTransaction(void)
CommandCounterIncrement(); CommandCounterIncrement();
/* Mark subtransaction as subcommitted */ /* Mark subtransaction as subcommitted */
if (TransactionIdIsValid(s->transactionId))
{
RecordSubTransactionCommit(); RecordSubTransactionCommit();
AtSubCommit_childXids(); AtSubCommit_childXids();
}
/* Post-commit cleanup */ /* Post-commit cleanup */
AfterTriggerEndSubXact(true); AfterTriggerEndSubXact(true);
AtSubCommit_Portals(s->parent->transactionIdData, AtSubCommit_Portals(s->subTransactionId,
s->parent->subTransactionId,
s->parent->curTransactionOwner); s->parent->curTransactionOwner);
AtEOSubXact_LargeObject(true, s->transactionIdData, AtEOSubXact_LargeObject(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtSubCommit_Notify(); AtSubCommit_Notify();
AtEOSubXact_UpdatePasswordFile(true, s->transactionIdData, AtEOSubXact_UpdatePasswordFile(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
CallXactCallbacks(XACT_EVENT_COMMIT_SUB, s->parent->transactionIdData); CallSubXactCallbacks(SUBXACT_EVENT_COMMIT_SUB, s->subTransactionId,
s->parent->subTransactionId);
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS,
true, false); true, false);
AtEOSubXact_RelationCache(true, s->transactionIdData, AtEOSubXact_RelationCache(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Inval(true); AtEOSubXact_Inval(true);
AtSubCommit_smgr(); AtSubCommit_smgr();
/*
* The only lock we actually release here is the subtransaction XID lock.
* The rest just get transferred to the parent resource owner.
*/
CurrentResourceOwner = s->curTransactionOwner;
if (TransactionIdIsValid(s->transactionId))
XactLockTableDelete(s->transactionId);
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_LOCKS, RESOURCE_RELEASE_LOCKS,
true, false); true, false);
...@@ -3035,13 +3233,13 @@ CommitSubTransaction(void) ...@@ -3035,13 +3233,13 @@ CommitSubTransaction(void)
true, false); true, false);
AtEOXact_GUC(true, true); AtEOXact_GUC(true, true);
AtEOSubXact_SPI(true, s->transactionIdData); AtEOSubXact_SPI(true, s->subTransactionId);
AtEOSubXact_on_commit_actions(true, s->transactionIdData, AtEOSubXact_on_commit_actions(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Namespace(true, s->transactionIdData, AtEOSubXact_Namespace(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Files(true, s->transactionIdData, AtEOSubXact_Files(true, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
/* /*
* We need to restore the upper transaction's read-only state, in case * We need to restore the upper transaction's read-only state, in case
...@@ -3058,6 +3256,8 @@ CommitSubTransaction(void) ...@@ -3058,6 +3256,8 @@ CommitSubTransaction(void)
AtSubCommit_Memory(); AtSubCommit_Memory();
s->state = TRANS_DEFAULT; s->state = TRANS_DEFAULT;
PopTransaction();
} }
/* /*
...@@ -3099,26 +3299,35 @@ AbortSubTransaction(void) ...@@ -3099,26 +3299,35 @@ AbortSubTransaction(void)
*/ */
AtSubAbort_Memory(); AtSubAbort_Memory();
/*
* We can skip all this stuff if the subxact failed before creating
* a ResourceOwner...
*/
if (s->curTransactionOwner)
{
AfterTriggerEndSubXact(false); AfterTriggerEndSubXact(false);
AtSubAbort_Portals(s->parent->transactionIdData, AtSubAbort_Portals(s->subTransactionId,
s->parent->subTransactionId,
s->parent->curTransactionOwner); s->parent->curTransactionOwner);
AtEOSubXact_LargeObject(false, s->transactionIdData, AtEOSubXact_LargeObject(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtSubAbort_Notify(); AtSubAbort_Notify();
AtEOSubXact_UpdatePasswordFile(false, s->transactionIdData, AtEOSubXact_UpdatePasswordFile(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
/* Advertise the fact that we aborted in pg_clog. */ /* Advertise the fact that we aborted in pg_clog. */
if (TransactionIdIsValid(s->transactionId))
RecordSubTransactionAbort(); RecordSubTransactionAbort();
/* Post-abort cleanup */ /* Post-abort cleanup */
CallXactCallbacks(XACT_EVENT_ABORT_SUB, s->parent->transactionIdData); CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId,
s->parent->subTransactionId);
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS,
false, false); false, false);
AtEOSubXact_RelationCache(false, s->transactionIdData, AtEOSubXact_RelationCache(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Inval(false); AtEOSubXact_Inval(false);
AtSubAbort_smgr(); AtSubAbort_smgr();
ResourceOwnerRelease(s->curTransactionOwner, ResourceOwnerRelease(s->curTransactionOwner,
...@@ -3129,13 +3338,14 @@ AbortSubTransaction(void) ...@@ -3129,13 +3338,14 @@ AbortSubTransaction(void)
false, false); false, false);
AtEOXact_GUC(false, true); AtEOXact_GUC(false, true);
AtEOSubXact_SPI(false, s->transactionIdData); AtEOSubXact_SPI(false, s->subTransactionId);
AtEOSubXact_on_commit_actions(false, s->transactionIdData, AtEOSubXact_on_commit_actions(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Namespace(false, s->transactionIdData, AtEOSubXact_Namespace(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
AtEOSubXact_Files(false, s->transactionIdData, AtEOSubXact_Files(false, s->subTransactionId,
s->parent->transactionIdData); s->parent->subTransactionId);
}
/* /*
* Reset user id which might have been changed transiently. Here we * Reset user id which might have been changed transiently. Here we
...@@ -3165,6 +3375,9 @@ AbortSubTransaction(void) ...@@ -3165,6 +3375,9 @@ AbortSubTransaction(void)
/* /*
* CleanupSubTransaction * CleanupSubTransaction
*
* The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function.
*/ */
static void static void
CleanupSubTransaction(void) CleanupSubTransaction(void)
...@@ -3177,67 +3390,24 @@ CleanupSubTransaction(void) ...@@ -3177,67 +3390,24 @@ CleanupSubTransaction(void)
elog(WARNING, "CleanupSubTransaction while in %s state", elog(WARNING, "CleanupSubTransaction while in %s state",
TransStateAsString(s->state)); TransStateAsString(s->state));
AtSubCleanup_Portals(); AtSubCleanup_Portals(s->subTransactionId);
CurrentResourceOwner = s->parent->curTransactionOwner; CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner; CurTransactionResourceOwner = s->parent->curTransactionOwner;
if (s->curTransactionOwner)
ResourceOwnerDelete(s->curTransactionOwner); ResourceOwnerDelete(s->curTransactionOwner);
s->curTransactionOwner = NULL; s->curTransactionOwner = NULL;
AtSubCleanup_Memory(); AtSubCleanup_Memory();
s->state = TRANS_DEFAULT; s->state = TRANS_DEFAULT;
}
/*
* StartAbortedSubTransaction
*
* This function is used to start a subtransaction and put it immediately
* into aborted state. The end result should be equivalent to
* StartSubTransaction immediately followed by AbortSubTransaction.
* The reason we don't implement it just that way is that many of the backend
* modules aren't designed to handle starting a subtransaction when not
* inside a valid transaction. Rather than making them all capable of
* doing that, we just omit the paired start and abort calls in this path.
*/
static void
StartAbortedSubTransaction(void)
{
TransactionState s = CurrentTransactionState;
if (s->state != TRANS_DEFAULT)
elog(WARNING, "StartAbortedSubTransaction while in %s state",
TransStateAsString(s->state));
s->state = TRANS_START;
/*
* We don't bother to generate a new Xid, so the end state is not
* *exactly* like we had done a full Start/AbortSubTransaction...
*/
s->transactionIdData = InvalidTransactionId;
/* Make sure currentUser is reasonably valid */
Assert(s->parent != NULL);
s->currentUser = s->parent->currentUser;
/*
* Initialize only what has to be there for CleanupSubTransaction to
* work.
*/
AtSubStart_Memory();
AtSubStart_ResourceOwner();
s->state = TRANS_ABORT;
AtSubAbort_Memory();
ShowTransactionState("StartAbortedSubTransaction"); PopTransaction();
} }
/* /*
* PushTransaction * PushTransaction
* Set up transaction state for a subtransaction * Create transaction state stack entry for a subtransaction
* *
* The caller has to make sure to always reassign CurrentTransactionState * The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function. * if it has a local pointer to it after calling this function.
...@@ -3247,6 +3417,13 @@ PushTransaction(void) ...@@ -3247,6 +3417,13 @@ PushTransaction(void)
{ {
TransactionState p = CurrentTransactionState; TransactionState p = CurrentTransactionState;
TransactionState s; TransactionState s;
AclId currentUser;
/*
* At present, GetUserId cannot fail, but let's not assume that. Get
* the ID before entering the critical code sequence.
*/
currentUser = GetUserId();
/* /*
* We keep subtransaction state nodes in TopTransactionContext. * We keep subtransaction state nodes in TopTransactionContext.
...@@ -3254,25 +3431,40 @@ PushTransaction(void) ...@@ -3254,25 +3431,40 @@ PushTransaction(void)
s = (TransactionState) s = (TransactionState)
MemoryContextAllocZero(TopTransactionContext, MemoryContextAllocZero(TopTransactionContext,
sizeof(TransactionStateData)); sizeof(TransactionStateData));
/*
* Assign a subtransaction ID, watching out for counter wraparound.
*/
currentSubTransactionId += 1;
if (currentSubTransactionId == InvalidSubTransactionId)
{
currentSubTransactionId -= 1;
pfree(s);
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("cannot have more than 2^32-1 subtransactions in a transaction")));
}
/*
* We can now stack a minimally valid subtransaction without fear of
* failure.
*/
s->transactionId = InvalidTransactionId; /* until assigned */
s->subTransactionId = currentSubTransactionId;
s->parent = p; s->parent = p;
s->nestingLevel = p->nestingLevel + 1; s->nestingLevel = p->nestingLevel + 1;
s->savepointLevel = p->savepointLevel; s->savepointLevel = p->savepointLevel;
s->state = TRANS_DEFAULT; s->state = TRANS_DEFAULT;
s->blockState = TBLOCK_SUBBEGIN; s->blockState = TBLOCK_SUBBEGIN;
s->currentUser = currentUser;
s->prevXactReadOnly = XactReadOnly;
/* Command IDs count in a continuous sequence through subtransactions */ CurrentTransactionState = s;
s->commandId = p->commandId;
/* /*
* Copy down some other data so that we will have valid state until * AbortSubTransaction and CleanupSubTransaction have to be able to
* StartSubTransaction runs. * cope with the subtransaction from here on out; in particular they
* should not assume that it necessarily has a transaction context,
* resource owner, or XID.
*/ */
s->transactionIdData = p->transactionIdData;
s->curTransactionContext = p->curTransactionContext;
s->curTransactionOwner = p->curTransactionOwner;
s->currentUser = p->currentUser;
CurrentTransactionState = s;
} }
/* /*
...@@ -3294,9 +3486,6 @@ PopTransaction(void) ...@@ -3294,9 +3486,6 @@ PopTransaction(void)
if (s->parent == NULL) if (s->parent == NULL)
elog(FATAL, "PopTransaction with no parent"); elog(FATAL, "PopTransaction with no parent");
/* Command IDs count in a continuous sequence through subtransactions */
s->parent->commandId = s->commandId;
CurrentTransactionState = s->parent; CurrentTransactionState = s->parent;
/* Let's just make sure CurTransactionContext is good */ /* Let's just make sure CurTransactionContext is good */
...@@ -3340,12 +3529,13 @@ ShowTransactionStateRec(TransactionState s) ...@@ -3340,12 +3529,13 @@ ShowTransactionStateRec(TransactionState s)
/* use ereport to suppress computation if msg will not be printed */ /* use ereport to suppress computation if msg will not be printed */
ereport(DEBUG2, ereport(DEBUG2,
(errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/cid: %u/%02u, nestlvl: %d, children: %s", (errmsg_internal("name: %s; blockState: %13s; state: %7s, xid/subid/cid: %u/%u/%u, nestlvl: %d, children: %s",
PointerIsValid(s->name) ? s->name : "unnamed", PointerIsValid(s->name) ? s->name : "unnamed",
BlockStateAsString(s->blockState), BlockStateAsString(s->blockState),
TransStateAsString(s->state), TransStateAsString(s->state),
(unsigned int) s->transactionIdData, (unsigned int) s->transactionId,
(unsigned int) s->commandId, (unsigned int) s->subTransactionId,
(unsigned int) currentCommandId,
s->nestingLevel, s->nestingLevel,
nodeToString(s->childXids)))); nodeToString(s->childXids))));
} }
...@@ -3371,8 +3561,10 @@ BlockStateAsString(TBlockState blockState) ...@@ -3371,8 +3561,10 @@ BlockStateAsString(TBlockState blockState)
return "END"; return "END";
case TBLOCK_ABORT: case TBLOCK_ABORT:
return "ABORT"; return "ABORT";
case TBLOCK_ENDABORT: case TBLOCK_ABORT_END:
return "ENDABORT"; return "ABORT END";
case TBLOCK_ABORT_PENDING:
return "ABORT PEND";
case TBLOCK_SUBBEGIN: case TBLOCK_SUBBEGIN:
return "SUB BEGIN"; return "SUB BEGIN";
case TBLOCK_SUBINPROGRESS: case TBLOCK_SUBINPROGRESS:
...@@ -3381,14 +3573,14 @@ BlockStateAsString(TBlockState blockState) ...@@ -3381,14 +3573,14 @@ BlockStateAsString(TBlockState blockState)
return "SUB END"; return "SUB END";
case TBLOCK_SUBABORT: case TBLOCK_SUBABORT:
return "SUB ABORT"; return "SUB ABORT";
case TBLOCK_SUBENDABORT_ALL: case TBLOCK_SUBABORT_END:
return "SUB ENDAB ALL"; return "SUB ABORT END";
case TBLOCK_SUBENDABORT:
return "SUB ENDAB";
case TBLOCK_SUBABORT_PENDING: case TBLOCK_SUBABORT_PENDING:
return "SUB ABRT PEND"; return "SUB ABRT PEND";
case TBLOCK_SUBENDABORT_RELEASE: case TBLOCK_SUBRESTART:
return "SUB ENDAB REL"; return "SUB RESTART";
case TBLOCK_SUBABORT_RESTART:
return "SUB AB RESTRT";
} }
return "UNRECOGNIZED"; return "UNRECOGNIZED";
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.169 2004/09/06 03:04:27 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.170 2004/09/16 16:58:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -747,7 +747,7 @@ begin:; ...@@ -747,7 +747,7 @@ begin:;
/* Insert record header */ /* Insert record header */
record->xl_prev = Insert->PrevRecord; record->xl_prev = Insert->PrevRecord;
record->xl_xid = GetCurrentTransactionId(); record->xl_xid = GetCurrentTransactionIdIfAny();
record->xl_len = len; /* doesn't include backup blocks */ record->xl_len = len; /* doesn't include backup blocks */
record->xl_info = info; record->xl_info = info;
record->xl_rmid = rmid; record->xl_rmid = rmid;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.70 2004/08/29 05:06:41 momjian Exp $ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.71 2004/09/16 16:58:27 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -108,16 +108,16 @@ static bool namespaceSearchPathValid = true; ...@@ -108,16 +108,16 @@ static bool namespaceSearchPathValid = true;
* in a particular backend session (this happens when a CREATE TEMP TABLE * in a particular backend session (this happens when a CREATE TEMP TABLE
* command is first executed). Thereafter it's the OID of the temp namespace. * command is first executed). Thereafter it's the OID of the temp namespace.
* *
* myTempNamespaceXID shows whether we've created the TEMP namespace in the * myTempNamespaceSubID shows whether we've created the TEMP namespace in the
* current transaction. The TransactionId propagates up the transaction tree, * current subtransaction. The flag propagates up the subtransaction tree,
* so the main transaction will correctly recognize the flag if all * so the main transaction will correctly recognize the flag if all
* intermediate subtransactions commit. When it is InvalidTransactionId, * intermediate subtransactions commit. When it is InvalidSubTransactionId,
* we either haven't made the TEMP namespace yet, or have successfully * we either haven't made the TEMP namespace yet, or have successfully
* committed its creation, depending on whether myTempNamespace is valid. * committed its creation, depending on whether myTempNamespace is valid.
*/ */
static Oid myTempNamespace = InvalidOid; static Oid myTempNamespace = InvalidOid;
static TransactionId myTempNamespaceXID = InvalidTransactionId; static SubTransactionId myTempNamespaceSubID = InvalidSubTransactionId;
/* /*
* "Special" namespace for CREATE SCHEMA. If set, it's the first search * "Special" namespace for CREATE SCHEMA. If set, it's the first search
...@@ -1696,8 +1696,8 @@ InitTempTableNamespace(void) ...@@ -1696,8 +1696,8 @@ InitTempTableNamespace(void)
myTempNamespace = namespaceId; myTempNamespace = namespaceId;
/* It should not be done already. */ /* It should not be done already. */
AssertState(myTempNamespaceXID == InvalidTransactionId); AssertState(myTempNamespaceSubID == InvalidSubTransactionId);
myTempNamespaceXID = GetCurrentTransactionId(); myTempNamespaceSubID = GetCurrentSubTransactionId();
namespaceSearchPathValid = false; /* need to rebuild list */ namespaceSearchPathValid = false; /* need to rebuild list */
} }
...@@ -1716,7 +1716,7 @@ AtEOXact_Namespace(bool isCommit) ...@@ -1716,7 +1716,7 @@ AtEOXact_Namespace(bool isCommit)
* temp tables at backend shutdown. (We only want to register the * temp tables at backend shutdown. (We only want to register the
* callback once per session, so this is a good place to do it.) * callback once per session, so this is a good place to do it.)
*/ */
if (myTempNamespaceXID == GetCurrentTransactionId()) if (myTempNamespaceSubID != InvalidSubTransactionId)
{ {
if (isCommit) if (isCommit)
on_shmem_exit(RemoveTempRelationsCallback, 0); on_shmem_exit(RemoveTempRelationsCallback, 0);
...@@ -1725,7 +1725,7 @@ AtEOXact_Namespace(bool isCommit) ...@@ -1725,7 +1725,7 @@ AtEOXact_Namespace(bool isCommit)
myTempNamespace = InvalidOid; myTempNamespace = InvalidOid;
namespaceSearchPathValid = false; /* need to rebuild list */ namespaceSearchPathValid = false; /* need to rebuild list */
} }
myTempNamespaceXID = InvalidTransactionId; myTempNamespaceSubID = InvalidSubTransactionId;
} }
/* /*
...@@ -1742,21 +1742,21 @@ AtEOXact_Namespace(bool isCommit) ...@@ -1742,21 +1742,21 @@ AtEOXact_Namespace(bool isCommit)
* AtEOSubXact_Namespace * AtEOSubXact_Namespace
* *
* At subtransaction commit, propagate the temp-namespace-creation * At subtransaction commit, propagate the temp-namespace-creation
* flag to the parent transaction. * flag to the parent subtransaction.
* *
* At subtransaction abort, forget the flag if we set it up. * At subtransaction abort, forget the flag if we set it up.
*/ */
void void
AtEOSubXact_Namespace(bool isCommit, TransactionId myXid, AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid) SubTransactionId parentSubid)
{ {
if (myTempNamespaceXID == myXid) if (myTempNamespaceSubID == mySubid)
{ {
if (isCommit) if (isCommit)
myTempNamespaceXID = parentXid; myTempNamespaceSubID = parentSubid;
else else
{ {
myTempNamespaceXID = InvalidTransactionId; myTempNamespaceSubID = InvalidSubTransactionId;
/* TEMP namespace creation failed, so reset state */ /* TEMP namespace creation failed, so reset state */
myTempNamespace = InvalidOid; myTempNamespace = InvalidOid;
namespaceSearchPathValid = false; /* need to rebuild list */ namespaceSearchPathValid = false; /* need to rebuild list */
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.35 2004/09/13 20:06:29 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.36 2004/09/16 16:58:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -309,7 +309,7 @@ PersistHoldablePortal(Portal portal) ...@@ -309,7 +309,7 @@ PersistHoldablePortal(Portal portal)
* If we're preserving a holdable portal, we had better be inside the * If we're preserving a holdable portal, we had better be inside the
* transaction that originally created it. * transaction that originally created it.
*/ */
Assert(portal->createXact == GetCurrentTransactionId()); Assert(portal->createSubid != InvalidSubTransactionId);
Assert(queryDesc != NULL); Assert(queryDesc != NULL);
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.116 2004/08/29 05:06:41 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.117 2004/09/16 16:58:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/resowner.h"
/* /*
* We don't want to log each fetching of a value from a sequence, * We don't want to log each fetching of a value from a sequence,
...@@ -754,24 +756,13 @@ static void ...@@ -754,24 +756,13 @@ static void
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel) init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
{ {
Oid relid = RangeVarGetRelid(relation, false); Oid relid = RangeVarGetRelid(relation, false);
TransactionId thisxid = GetCurrentTransactionId(); TransactionId thisxid = GetTopTransactionId();
SeqTable elm; volatile SeqTable elm;
Relation seqrel; Relation seqrel;
/* Look to see if we already have a seqtable entry for relation */
for (elm = seqtab; elm != NULL; elm = elm->next)
{
if (elm->relid == relid)
break;
}
/* /*
* Open the sequence relation, acquiring AccessShareLock if we don't * Open the sequence relation.
* already have a lock in the current xact.
*/ */
if (elm == NULL || elm->xid != thisxid)
seqrel = relation_open(relid, AccessShareLock);
else
seqrel = relation_open(relid, NoLock); seqrel = relation_open(relid, NoLock);
if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE) if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
...@@ -780,6 +771,13 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel) ...@@ -780,6 +771,13 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
errmsg("\"%s\" is not a sequence", errmsg("\"%s\" is not a sequence",
relation->relname))); relation->relname)));
/* Look to see if we already have a seqtable entry for relation */
for (elm = seqtab; elm != NULL; elm = elm->next)
{
if (elm->relid == relid)
break;
}
/* /*
* Allocate new seqtable entry if we didn't find one. * Allocate new seqtable entry if we didn't find one.
* *
...@@ -799,14 +797,42 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel) ...@@ -799,14 +797,42 @@ init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
(errcode(ERRCODE_OUT_OF_MEMORY), (errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory"))); errmsg("out of memory")));
elm->relid = relid; elm->relid = relid;
elm->xid = InvalidTransactionId;
/* increment is set to 0 until we do read_info (see currval) */ /* increment is set to 0 until we do read_info (see currval) */
elm->last = elm->cached = elm->increment = 0; elm->last = elm->cached = elm->increment = 0;
elm->next = seqtab; elm->next = seqtab;
seqtab = elm; seqtab = elm;
} }
/*
* If we haven't touched the sequence already in this transaction,
* we need to acquire AccessShareLock. We arrange for the lock to
* be owned by the top transaction, so that we don't need to do it
* more than once per xact.
*/
if (elm->xid != thisxid)
{
ResourceOwner currentOwner;
currentOwner = CurrentResourceOwner;
PG_TRY();
{
CurrentResourceOwner = TopTransactionResourceOwner;
LockRelation(seqrel, AccessShareLock);
}
PG_CATCH();
{
/* Ensure CurrentResourceOwner is restored on error */
CurrentResourceOwner = currentOwner;
PG_RE_THROW();
}
PG_END_TRY();
CurrentResourceOwner = currentOwner;
/* Flag that we have a lock in the current xact. */ /* Flag that we have a lock in the current xact. */
elm->xid = thisxid; elm->xid = thisxid;
}
*p_elm = elm; *p_elm = elm;
*p_rel = seqrel; *p_rel = seqrel;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.131 2004/08/31 23:27:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.132 2004/09/16 16:58:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -71,14 +71,14 @@ typedef struct OnCommitItem ...@@ -71,14 +71,14 @@ typedef struct OnCommitItem
OnCommitAction oncommit; /* what to do at end of xact */ OnCommitAction oncommit; /* what to do at end of xact */
/* /*
* If this entry was created during this xact, it should be deleted at * If this entry was created during the current transaction,
* xact abort. Conversely, if this entry was deleted during this * creating_subid is the ID of the creating subxact; if created in a prior
* xact, it should be removed at xact commit. We leave deleted * transaction, creating_subid is zero. If deleted during the current
* entries in the list until commit so that we can roll back if * transaction, deleting_subid is the ID of the deleting subxact; if no
* needed. * deletion request is pending, deleting_subid is zero.
*/ */
TransactionId creating_xid; SubTransactionId creating_subid;
TransactionId deleting_xid; SubTransactionId deleting_subid;
} OnCommitItem; } OnCommitItem;
static List *on_commits = NIL; static List *on_commits = NIL;
...@@ -5821,8 +5821,8 @@ register_on_commit_action(Oid relid, OnCommitAction action) ...@@ -5821,8 +5821,8 @@ register_on_commit_action(Oid relid, OnCommitAction action)
oc = (OnCommitItem *) palloc(sizeof(OnCommitItem)); oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
oc->relid = relid; oc->relid = relid;
oc->oncommit = action; oc->oncommit = action;
oc->creating_xid = GetCurrentTransactionId(); oc->creating_subid = GetCurrentSubTransactionId();
oc->deleting_xid = InvalidTransactionId; oc->deleting_subid = InvalidSubTransactionId;
on_commits = lcons(oc, on_commits); on_commits = lcons(oc, on_commits);
...@@ -5845,7 +5845,7 @@ remove_on_commit_action(Oid relid) ...@@ -5845,7 +5845,7 @@ remove_on_commit_action(Oid relid)
if (oc->relid == relid) if (oc->relid == relid)
{ {
oc->deleting_xid = GetCurrentTransactionId(); oc->deleting_subid = GetCurrentSubTransactionId();
break; break;
} }
} }
...@@ -5860,7 +5860,6 @@ remove_on_commit_action(Oid relid) ...@@ -5860,7 +5860,6 @@ remove_on_commit_action(Oid relid)
void void
PreCommit_on_commit_actions(void) PreCommit_on_commit_actions(void)
{ {
TransactionId xid = GetCurrentTransactionId();
ListCell *l; ListCell *l;
foreach(l, on_commits) foreach(l, on_commits)
...@@ -5868,7 +5867,7 @@ PreCommit_on_commit_actions(void) ...@@ -5868,7 +5867,7 @@ PreCommit_on_commit_actions(void)
OnCommitItem *oc = (OnCommitItem *) lfirst(l); OnCommitItem *oc = (OnCommitItem *) lfirst(l);
/* Ignore entry if already dropped in this xact */ /* Ignore entry if already dropped in this xact */
if (oc->deleting_xid == xid) if (oc->deleting_subid != InvalidSubTransactionId)
continue; continue;
switch (oc->oncommit) switch (oc->oncommit)
...@@ -5895,7 +5894,7 @@ PreCommit_on_commit_actions(void) ...@@ -5895,7 +5894,7 @@ PreCommit_on_commit_actions(void)
* remove_on_commit_action, so the entry should get * remove_on_commit_action, so the entry should get
* marked as deleted. * marked as deleted.
*/ */
Assert(oc->deleting_xid == xid); Assert(oc->deleting_subid != InvalidSubTransactionId);
break; break;
} }
} }
...@@ -5911,7 +5910,7 @@ PreCommit_on_commit_actions(void) ...@@ -5911,7 +5910,7 @@ PreCommit_on_commit_actions(void)
* during abort, remove those created during this transaction. * during abort, remove those created during this transaction.
*/ */
void void
AtEOXact_on_commit_actions(bool isCommit, TransactionId xid) AtEOXact_on_commit_actions(bool isCommit)
{ {
ListCell *cur_item; ListCell *cur_item;
ListCell *prev_item; ListCell *prev_item;
...@@ -5923,8 +5922,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid) ...@@ -5923,8 +5922,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid)
{ {
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (isCommit ? TransactionIdEquals(oc->deleting_xid, xid) : if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
TransactionIdEquals(oc->creating_xid, xid)) oc->creating_subid != InvalidSubTransactionId)
{ {
/* cur_item must be removed */ /* cur_item must be removed */
on_commits = list_delete_cell(on_commits, cur_item, prev_item); on_commits = list_delete_cell(on_commits, cur_item, prev_item);
...@@ -5937,8 +5936,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid) ...@@ -5937,8 +5936,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid)
else else
{ {
/* cur_item must be preserved */ /* cur_item must be preserved */
oc->creating_xid = InvalidTransactionId; oc->creating_subid = InvalidSubTransactionId;
oc->deleting_xid = InvalidTransactionId; oc->deleting_subid = InvalidSubTransactionId;
prev_item = cur_item; prev_item = cur_item;
cur_item = lnext(prev_item); cur_item = lnext(prev_item);
} }
...@@ -5953,8 +5952,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid) ...@@ -5953,8 +5952,8 @@ AtEOXact_on_commit_actions(bool isCommit, TransactionId xid)
* this subtransaction as being the parent's responsibility. * this subtransaction as being the parent's responsibility.
*/ */
void void
AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid, AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid) SubTransactionId parentSubid)
{ {
ListCell *cur_item; ListCell *cur_item;
ListCell *prev_item; ListCell *prev_item;
...@@ -5966,7 +5965,7 @@ AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid, ...@@ -5966,7 +5965,7 @@ AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid,
{ {
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (!isCommit && TransactionIdEquals(oc->creating_xid, childXid)) if (!isCommit && oc->creating_subid == mySubid)
{ {
/* cur_item must be removed */ /* cur_item must be removed */
on_commits = list_delete_cell(on_commits, cur_item, prev_item); on_commits = list_delete_cell(on_commits, cur_item, prev_item);
...@@ -5979,10 +5978,10 @@ AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid, ...@@ -5979,10 +5978,10 @@ AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid,
else else
{ {
/* cur_item must be preserved */ /* cur_item must be preserved */
if (TransactionIdEquals(oc->creating_xid, childXid)) if (oc->creating_subid == mySubid)
oc->creating_xid = parentXid; oc->creating_subid = parentSubid;
if (TransactionIdEquals(oc->deleting_xid, childXid)) if (oc->deleting_subid == mySubid)
oc->deleting_xid = isCommit ? parentXid : InvalidTransactionId; oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
prev_item = cur_item; prev_item = cur_item;
cur_item = lnext(prev_item); cur_item = lnext(prev_item);
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.144 2004/08/29 05:06:41 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.145 2004/09/16 16:58:28 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,28 +45,30 @@ ...@@ -45,28 +45,30 @@
extern bool Password_encryption; extern bool Password_encryption;
/* /*
* The need-to-update-files flags are a pair of TransactionIds that show what * The need-to-update-files flags are a pair of SubTransactionIds that show
* level of the transaction tree requested the update. To register an update, * what level of the subtransaction tree requested the update. To register
* the transaction saves its own TransactionId in the flag, unless the value * an update, the subtransaction saves its own SubTransactionId in the flag,
* was already set to a valid TransactionId. If it aborts and the value is its * unless the value was already set to a valid SubTransactionId (which implies
* TransactionId, it resets the value to InvalidTransactionId. If it commits, * that it or a parent level has already requested the same). If it aborts
* it changes the value to its parent's TransactionId. This way the value is * and the value is its SubTransactionId, it resets the flag to
* propagated up to the topmost transaction, which will update the files if a * InvalidSubTransactionId. If it commits, it changes the value to its
* valid TransactionId is detected. * parent's SubTransactionId. This way the value is propagated up to the
*/ * top-level transaction, which will update the files if a valid
static TransactionId user_file_update_xid = InvalidTransactionId; * SubTransactionId is detected.
static TransactionId group_file_update_xid = InvalidTransactionId; */
static SubTransactionId user_file_update_subid = InvalidSubTransactionId;
static SubTransactionId group_file_update_subid = InvalidSubTransactionId;
#define user_file_update_needed() \ #define user_file_update_needed() \
do { \ do { \
if (user_file_update_xid == InvalidTransactionId) \ if (user_file_update_subid == InvalidSubTransactionId) \
user_file_update_xid = GetCurrentTransactionId(); \ user_file_update_subid = GetCurrentSubTransactionId(); \
} while (0) } while (0)
#define group_file_update_needed() \ #define group_file_update_needed() \
do { \ do { \
if (group_file_update_xid == InvalidTransactionId) \ if (group_file_update_subid == InvalidSubTransactionId) \
group_file_update_xid = GetCurrentTransactionId(); \ group_file_update_subid = GetCurrentSubTransactionId(); \
} while (0) } while (0)
...@@ -451,14 +453,14 @@ AtEOXact_UpdatePasswordFile(bool isCommit) ...@@ -451,14 +453,14 @@ AtEOXact_UpdatePasswordFile(bool isCommit)
Relation urel = NULL; Relation urel = NULL;
Relation grel = NULL; Relation grel = NULL;
if (user_file_update_xid == InvalidTransactionId && if (user_file_update_subid == InvalidSubTransactionId &&
group_file_update_xid == InvalidTransactionId) group_file_update_subid == InvalidSubTransactionId)
return; return;
if (!isCommit) if (!isCommit)
{ {
user_file_update_xid = InvalidTransactionId; user_file_update_subid = InvalidSubTransactionId;
group_file_update_xid = InvalidTransactionId; group_file_update_subid = InvalidSubTransactionId;
return; return;
} }
...@@ -470,22 +472,22 @@ AtEOXact_UpdatePasswordFile(bool isCommit) ...@@ -470,22 +472,22 @@ AtEOXact_UpdatePasswordFile(bool isCommit)
* pg_shadow or pg_group, which likely won't have gotten a strong * pg_shadow or pg_group, which likely won't have gotten a strong
* enough lock), so get the locks we need before writing anything. * enough lock), so get the locks we need before writing anything.
*/ */
if (user_file_update_xid != InvalidTransactionId) if (user_file_update_subid != InvalidSubTransactionId)
urel = heap_openr(ShadowRelationName, ExclusiveLock); urel = heap_openr(ShadowRelationName, ExclusiveLock);
if (group_file_update_xid != InvalidTransactionId) if (group_file_update_subid != InvalidSubTransactionId)
grel = heap_openr(GroupRelationName, ExclusiveLock); grel = heap_openr(GroupRelationName, ExclusiveLock);
/* Okay to write the files */ /* Okay to write the files */
if (user_file_update_xid != InvalidTransactionId) if (user_file_update_subid != InvalidSubTransactionId)
{ {
user_file_update_xid = InvalidTransactionId; user_file_update_subid = InvalidSubTransactionId;
write_user_file(urel); write_user_file(urel);
heap_close(urel, NoLock); heap_close(urel, NoLock);
} }
if (group_file_update_xid != InvalidTransactionId) if (group_file_update_subid != InvalidSubTransactionId)
{ {
group_file_update_xid = InvalidTransactionId; group_file_update_subid = InvalidSubTransactionId;
write_group_file(grel); write_group_file(grel);
heap_close(grel, NoLock); heap_close(grel, NoLock);
} }
...@@ -503,24 +505,25 @@ AtEOXact_UpdatePasswordFile(bool isCommit) ...@@ -503,24 +505,25 @@ AtEOXact_UpdatePasswordFile(bool isCommit)
* need-to-update-files flags. * need-to-update-files flags.
*/ */
void void
AtEOSubXact_UpdatePasswordFile(bool isCommit, TransactionId myXid, AtEOSubXact_UpdatePasswordFile(bool isCommit,
TransactionId parentXid) SubTransactionId mySubid,
SubTransactionId parentSubid)
{ {
if (isCommit) if (isCommit)
{ {
if (user_file_update_xid == myXid) if (user_file_update_subid == mySubid)
user_file_update_xid = parentXid; user_file_update_subid = parentSubid;
if (group_file_update_xid == myXid) if (group_file_update_subid == mySubid)
group_file_update_xid = parentXid; group_file_update_subid = parentSubid;
} }
else else
{ {
if (user_file_update_xid == myXid) if (user_file_update_subid == mySubid)
user_file_update_xid = InvalidTransactionId; user_file_update_subid = InvalidSubTransactionId;
if (group_file_update_xid == myXid) if (group_file_update_subid == mySubid)
group_file_update_xid = InvalidTransactionId; group_file_update_subid = InvalidSubTransactionId;
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.127 2004/09/13 20:06:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.128 2004/09/16 16:58:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -104,7 +104,7 @@ SPI_connect(void) ...@@ -104,7 +104,7 @@ SPI_connect(void)
_SPI_current = &(_SPI_stack[_SPI_connected]); _SPI_current = &(_SPI_stack[_SPI_connected]);
_SPI_current->processed = 0; _SPI_current->processed = 0;
_SPI_current->tuptable = NULL; _SPI_current->tuptable = NULL;
_SPI_current->connectXid = GetCurrentTransactionId(); _SPI_current->connectSubid = GetCurrentSubTransactionId();
/* /*
* Create memory contexts for this procedure * Create memory contexts for this procedure
...@@ -198,10 +198,10 @@ AtEOXact_SPI(bool isCommit) ...@@ -198,10 +198,10 @@ AtEOXact_SPI(bool isCommit)
* Clean up SPI state at subtransaction commit or abort. * Clean up SPI state at subtransaction commit or abort.
* *
* During commit, there shouldn't be any unclosed entries remaining from * During commit, there shouldn't be any unclosed entries remaining from
* the current transaction; we throw them away if found. * the current subtransaction; we emit a warning if any are found.
*/ */
void void
AtEOSubXact_SPI(bool isCommit, TransactionId childXid) AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid)
{ {
bool found = false; bool found = false;
...@@ -209,7 +209,7 @@ AtEOSubXact_SPI(bool isCommit, TransactionId childXid) ...@@ -209,7 +209,7 @@ AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
{ {
_SPI_connection *connection = &(_SPI_stack[_SPI_connected]); _SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
if (connection->connectXid != childXid) if (connection->connectSubid != mySubid)
break; /* couldn't be any underneath it either */ break; /* couldn't be any underneath it either */
found = true; found = true;
...@@ -235,7 +235,7 @@ AtEOSubXact_SPI(bool isCommit, TransactionId childXid) ...@@ -235,7 +235,7 @@ AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_WARNING), (errcode(ERRCODE_WARNING),
errmsg("subtransaction left non-empty SPI stack"), errmsg("subtransaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls"))); errhint("Check for missing \"SPI_finish\" calls.")));
} }
...@@ -1692,8 +1692,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location) ...@@ -1692,8 +1692,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
parentcxt = _SPI_current->procCxt; parentcxt = _SPI_current->procCxt;
else if (location == _SPI_CPLAN_TOPCXT) else if (location == _SPI_CPLAN_TOPCXT)
parentcxt = TopMemoryContext; parentcxt = TopMemoryContext;
else else /* (this case not currently used) */
/* (this case not currently used) */
parentcxt = CurrentMemoryContext; parentcxt = CurrentMemoryContext;
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.75 2004/09/11 15:56:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/libpq/be-fsstubs.c,v 1.76 2004/09/16 16:58:30 tgl Exp $
* *
* NOTES * NOTES
* This should be moved to a more appropriate place. It is here * This should be moved to a more appropriate place. It is here
...@@ -551,11 +551,11 @@ AtEOXact_LargeObject(bool isCommit) ...@@ -551,11 +551,11 @@ AtEOXact_LargeObject(bool isCommit)
* Take care of large objects at subtransaction commit/abort * Take care of large objects at subtransaction commit/abort
* *
* Reassign LOs created/opened during a committing subtransaction * Reassign LOs created/opened during a committing subtransaction
* to the parent transaction. On abort, just close them. * to the parent subtransaction. On abort, just close them.
*/ */
void void
AtEOSubXact_LargeObject(bool isCommit, TransactionId myXid, AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid) SubTransactionId parentSubid)
{ {
int i; int i;
...@@ -566,10 +566,10 @@ AtEOSubXact_LargeObject(bool isCommit, TransactionId myXid, ...@@ -566,10 +566,10 @@ AtEOSubXact_LargeObject(bool isCommit, TransactionId myXid,
{ {
LargeObjectDesc *lo = cookies[i]; LargeObjectDesc *lo = cookies[i];
if (lo != NULL && lo->xid == myXid) if (lo != NULL && lo->subid == mySubid)
{ {
if (isCommit) if (isCommit)
lo->xid = parentXid; lo->subid = parentSubid;
else else
{ {
/* /*
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.47 2004/08/29 05:06:47 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.48 2004/09/16 16:58:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -269,7 +269,7 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck, ...@@ -269,7 +269,7 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck,
if (!strategy_hint_vacuum) if (!strategy_hint_vacuum)
{ {
if (!cdb->t1_vacuum && if (!cdb->t1_vacuum &&
!TransactionIdIsCurrentTransactionId(cdb->t1_xid)) !TransactionIdEquals(cdb->t1_xid, GetTopTransactionId()))
{ {
STRAT_LIST_REMOVE(cdb); STRAT_LIST_REMOVE(cdb);
STRAT_MRU_INSERT(cdb, STRAT_LIST_T2); STRAT_MRU_INSERT(cdb, STRAT_LIST_T2);
...@@ -286,7 +286,7 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck, ...@@ -286,7 +286,7 @@ StrategyBufferLookup(BufferTag *tagPtr, bool recheck,
*/ */
if (cdb->t1_vacuum) if (cdb->t1_vacuum)
{ {
cdb->t1_xid = GetCurrentTransactionId(); cdb->t1_xid = GetTopTransactionId();
cdb->t1_vacuum = false; cdb->t1_vacuum = false;
} }
} }
...@@ -644,7 +644,8 @@ StrategyReplaceBuffer(BufferDesc *buf, BufferTag *newTag, ...@@ -644,7 +644,8 @@ StrategyReplaceBuffer(BufferDesc *buf, BufferTag *newTag,
*/ */
if (strategy_hint_vacuum) if (strategy_hint_vacuum)
{ {
if (TransactionIdIsCurrentTransactionId(strategy_vacuum_xid)) if (TransactionIdEquals(strategy_vacuum_xid,
GetTopTransactionId()))
STRAT_LRU_INSERT(cdb_found, STRAT_LIST_T1); STRAT_LRU_INSERT(cdb_found, STRAT_LIST_T1);
else else
{ {
...@@ -661,7 +662,7 @@ StrategyReplaceBuffer(BufferDesc *buf, BufferTag *newTag, ...@@ -661,7 +662,7 @@ StrategyReplaceBuffer(BufferDesc *buf, BufferTag *newTag,
* single UPDATE promoting a newcomer straight into T2. Also * single UPDATE promoting a newcomer straight into T2. Also
* remember if it was loaded for VACUUM. * remember if it was loaded for VACUUM.
*/ */
cdb_found->t1_xid = GetCurrentTransactionId(); cdb_found->t1_xid = GetTopTransactionId();
cdb_found->t1_vacuum = strategy_hint_vacuum; cdb_found->t1_vacuum = strategy_hint_vacuum;
} }
} }
...@@ -727,7 +728,7 @@ void ...@@ -727,7 +728,7 @@ void
StrategyHintVacuum(bool vacuum_active) StrategyHintVacuum(bool vacuum_active)
{ {
strategy_hint_vacuum = vacuum_active; strategy_hint_vacuum = vacuum_active;
strategy_vacuum_xid = GetCurrentTransactionId(); strategy_vacuum_xid = GetTopTransactionId();
} }
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.112 2004/08/29 05:06:47 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.113 2004/09/16 16:58:32 tgl Exp $
* *
* NOTES: * NOTES:
* *
...@@ -123,7 +123,7 @@ typedef struct vfd ...@@ -123,7 +123,7 @@ typedef struct vfd
{ {
signed short fd; /* current FD, or VFD_CLOSED if none */ signed short fd; /* current FD, or VFD_CLOSED if none */
unsigned short fdstate; /* bitflags for VFD's state */ unsigned short fdstate; /* bitflags for VFD's state */
TransactionId create_xid; /* for XACT_TEMPORARY fds, creating Xid */ SubTransactionId create_subid; /* for TEMPORARY fds, creating subxact */
File nextFree; /* link to next free VFD, if in freelist */ File nextFree; /* link to next free VFD, if in freelist */
File lruMoreRecently; /* doubly linked recency-of-use list */ File lruMoreRecently; /* doubly linked recency-of-use list */
File lruLessRecently; File lruLessRecently;
...@@ -171,7 +171,7 @@ typedef struct ...@@ -171,7 +171,7 @@ typedef struct
FILE *file; FILE *file;
DIR *dir; DIR *dir;
} desc; } desc;
TransactionId create_xid; SubTransactionId create_subid;
} AllocateDesc; } AllocateDesc;
static int numAllocatedDescs = 0; static int numAllocatedDescs = 0;
...@@ -887,7 +887,7 @@ OpenTemporaryFile(bool interXact) ...@@ -887,7 +887,7 @@ OpenTemporaryFile(bool interXact)
if (!interXact) if (!interXact)
{ {
VfdCache[file].fdstate |= FD_XACT_TEMPORARY; VfdCache[file].fdstate |= FD_XACT_TEMPORARY;
VfdCache[file].create_xid = GetCurrentTransactionId(); VfdCache[file].create_subid = GetCurrentSubTransactionId();
} }
return file; return file;
...@@ -1166,7 +1166,7 @@ TryAgain: ...@@ -1166,7 +1166,7 @@ TryAgain:
desc->kind = AllocateDescFile; desc->kind = AllocateDescFile;
desc->desc.file = file; desc->desc.file = file;
desc->create_xid = GetCurrentTransactionId(); desc->create_subid = GetCurrentSubTransactionId();
numAllocatedDescs++; numAllocatedDescs++;
return desc->desc.file; return desc->desc.file;
} }
...@@ -1281,7 +1281,7 @@ TryAgain: ...@@ -1281,7 +1281,7 @@ TryAgain:
desc->kind = AllocateDescDir; desc->kind = AllocateDescDir;
desc->desc.dir = dir; desc->desc.dir = dir;
desc->create_xid = GetCurrentTransactionId(); desc->create_subid = GetCurrentSubTransactionId();
numAllocatedDescs++; numAllocatedDescs++;
return desc->desc.dir; return desc->desc.dir;
} }
...@@ -1359,10 +1359,11 @@ closeAllVfds(void) ...@@ -1359,10 +1359,11 @@ closeAllVfds(void)
* *
* Take care of subtransaction commit/abort. At abort, we close temp files * Take care of subtransaction commit/abort. At abort, we close temp files
* that the subtransaction may have opened. At commit, we reassign the * that the subtransaction may have opened. At commit, we reassign the
* files that were opened to the parent transaction. * files that were opened to the parent subtransaction.
*/ */
void void
AtEOSubXact_Files(bool isCommit, TransactionId myXid, TransactionId parentXid) AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
SubTransactionId parentSubid)
{ {
Index i; Index i;
...@@ -1374,10 +1375,10 @@ AtEOSubXact_Files(bool isCommit, TransactionId myXid, TransactionId parentXid) ...@@ -1374,10 +1375,10 @@ AtEOSubXact_Files(bool isCommit, TransactionId myXid, TransactionId parentXid)
unsigned short fdstate = VfdCache[i].fdstate; unsigned short fdstate = VfdCache[i].fdstate;
if ((fdstate & FD_XACT_TEMPORARY) && if ((fdstate & FD_XACT_TEMPORARY) &&
VfdCache[i].create_xid == myXid) VfdCache[i].create_subid == mySubid)
{ {
if (isCommit) if (isCommit)
VfdCache[i].create_xid = parentXid; VfdCache[i].create_subid = parentSubid;
else if (VfdCache[i].fileName != NULL) else if (VfdCache[i].fileName != NULL)
FileClose(i); FileClose(i);
} }
...@@ -1386,10 +1387,10 @@ AtEOSubXact_Files(bool isCommit, TransactionId myXid, TransactionId parentXid) ...@@ -1386,10 +1387,10 @@ AtEOSubXact_Files(bool isCommit, TransactionId myXid, TransactionId parentXid)
for (i = 0; i < numAllocatedDescs; i++) for (i = 0; i < numAllocatedDescs; i++)
{ {
if (allocatedDescs[i].create_xid == myXid) if (allocatedDescs[i].create_subid == mySubid)
{ {
if (isCommit) if (isCommit)
allocatedDescs[i].create_xid = parentXid; allocatedDescs[i].create_subid = parentSubid;
else else
{ {
/* have to recheck the item after FreeDesc (ugly) */ /* have to recheck the item after FreeDesc (ugly) */
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.106 2004/08/29 05:06:48 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.107 2004/09/16 16:58:33 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -167,7 +167,7 @@ inv_create(int flags) ...@@ -167,7 +167,7 @@ inv_create(int flags)
retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc)); retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
retval->id = file_oid; retval->id = file_oid;
retval->xid = GetCurrentTransactionId(); retval->subid = GetCurrentSubTransactionId();
retval->offset = 0; retval->offset = 0;
if (flags & INV_WRITE) if (flags & INV_WRITE)
...@@ -199,7 +199,7 @@ inv_open(Oid lobjId, int flags) ...@@ -199,7 +199,7 @@ inv_open(Oid lobjId, int flags)
retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc)); retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
retval->id = lobjId; retval->id = lobjId;
retval->xid = GetCurrentTransactionId(); retval->subid = GetCurrentSubTransactionId();
retval->offset = 0; retval->offset = 0;
if (flags & INV_WRITE) if (flags & INV_WRITE)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.69 2004/08/29 05:06:48 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.70 2004/09/16 16:58:33 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/sinval.h"
#include "utils/inval.h" #include "utils/inval.h"
...@@ -311,9 +312,6 @@ UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode) ...@@ -311,9 +312,6 @@ UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
* Insert a lock showing that the given transaction ID is running --- * Insert a lock showing that the given transaction ID is running ---
* this is done during xact startup. The lock can then be used to wait * this is done during xact startup. The lock can then be used to wait
* for the transaction to finish. * for the transaction to finish.
*
* We need no corresponding unlock function, since the lock will always
* be released implicitly at transaction commit/abort, never any other way.
*/ */
void void
XactLockTableInsert(TransactionId xid) XactLockTableInsert(TransactionId xid)
...@@ -325,19 +323,43 @@ XactLockTableInsert(TransactionId xid) ...@@ -325,19 +323,43 @@ XactLockTableInsert(TransactionId xid)
tag.dbId = InvalidOid; /* xids are globally unique */ tag.dbId = InvalidOid; /* xids are globally unique */
tag.objId.xid = xid; tag.objId.xid = xid;
if (!LockAcquire(LockTableId, &tag, xid, if (!LockAcquire(LockTableId, &tag, GetTopTransactionId(),
ExclusiveLock, false)) ExclusiveLock, false))
elog(ERROR, "LockAcquire failed"); elog(ERROR, "LockAcquire failed");
} }
/*
* XactLockTableDelete
*
* Delete the lock showing that the given transaction ID is running.
* (This is never used for main transaction IDs; those locks are only
* released implicitly at transaction end. But we do use it for subtrans
* IDs.)
*/
void
XactLockTableDelete(TransactionId xid)
{
LOCKTAG tag;
MemSet(&tag, 0, sizeof(tag));
tag.relId = XactLockTableId;
tag.dbId = InvalidOid; /* xids are globally unique */
tag.objId.xid = xid;
LockRelease(LockTableId, &tag, GetTopTransactionId(), ExclusiveLock);
}
/* /*
* XactLockTableWait * XactLockTableWait
* *
* Wait for the specified transaction to commit or abort. * Wait for the specified transaction to commit or abort.
* *
* Note that this does the right thing for subtransactions: if we * Note that this does the right thing for subtransactions: if we wait on a
* wait on a subtransaction, we will be awakened as soon as it aborts * subtransaction, we will exit as soon as it aborts or its top parent commits.
* or its parent commits. * It takes some extra work to ensure this, because to save on shared memory
* the XID lock of a subtransaction is released when it ends, whether
* successfully or unsuccessfully. So we have to check if it's "still running"
* and if so wait for its parent.
*/ */
void void
XactLockTableWait(TransactionId xid) XactLockTableWait(TransactionId xid)
...@@ -345,6 +367,9 @@ XactLockTableWait(TransactionId xid) ...@@ -345,6 +367,9 @@ XactLockTableWait(TransactionId xid)
LOCKTAG tag; LOCKTAG tag;
TransactionId myxid = GetTopTransactionId(); TransactionId myxid = GetTopTransactionId();
for (;;)
{
Assert(TransactionIdIsValid(xid));
Assert(!TransactionIdEquals(xid, myxid)); Assert(!TransactionIdEquals(xid, myxid));
MemSet(&tag, 0, sizeof(tag)); MemSet(&tag, 0, sizeof(tag));
...@@ -352,12 +377,15 @@ XactLockTableWait(TransactionId xid) ...@@ -352,12 +377,15 @@ XactLockTableWait(TransactionId xid)
tag.dbId = InvalidOid; tag.dbId = InvalidOid;
tag.objId.xid = xid; tag.objId.xid = xid;
if (!LockAcquire(LockTableId, &tag, myxid, if (!LockAcquire(LockTableId, &tag, myxid, ShareLock, false))
ShareLock, false))
elog(ERROR, "LockAcquire failed"); elog(ERROR, "LockAcquire failed");
LockRelease(LockTableId, &tag, myxid, ShareLock); LockRelease(LockTableId, &tag, myxid, ShareLock);
if (!TransactionIdIsInProgress(xid))
break;
xid = SubTransGetParent(xid);
}
/* /*
* Transaction was committed/aborted/crashed - we have to update * Transaction was committed/aborted/crashed - we have to update
* pg_clog if transaction is still marked as running. * pg_clog if transaction is still marked as running.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/xid.c,v 1.5 2004/08/29 04:12:52 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/xid.c,v 1.6 2004/09/16 16:58:34 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -93,7 +93,7 @@ Datum ...@@ -93,7 +93,7 @@ Datum
xid_age(PG_FUNCTION_ARGS) xid_age(PG_FUNCTION_ARGS)
{ {
TransactionId xid = PG_GETARG_TRANSACTIONID(0); TransactionId xid = PG_GETARG_TRANSACTIONID(0);
TransactionId now = GetCurrentTransactionId(); TransactionId now = GetTopTransactionId();
/* Permanent XIDs are always infinitely old */ /* Permanent XIDs are always infinitely old */
if (!TransactionIdIsNormal(xid)) if (!TransactionIdIsNormal(xid))
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.210 2004/08/29 05:06:50 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.211 2004/09/16 16:58:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -836,7 +836,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, ...@@ -836,7 +836,7 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
*/ */
relation->rd_refcnt = 0; relation->rd_refcnt = 0;
relation->rd_isnailed = false; relation->rd_isnailed = false;
relation->rd_createxact = InvalidTransactionId; relation->rd_createSubid = InvalidSubTransactionId;
relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace); relation->rd_istemp = isTempNamespace(relation->rd_rel->relnamespace);
/* /*
...@@ -1287,7 +1287,7 @@ formrdesc(const char *relationName, ...@@ -1287,7 +1287,7 @@ formrdesc(const char *relationName,
* for new or temp relations. * for new or temp relations.
*/ */
relation->rd_isnailed = true; relation->rd_isnailed = true;
relation->rd_createxact = InvalidTransactionId; relation->rd_createSubid = InvalidSubTransactionId;
relation->rd_istemp = false; relation->rd_istemp = false;
/* /*
...@@ -1578,7 +1578,7 @@ RelationClose(Relation relation) ...@@ -1578,7 +1578,7 @@ RelationClose(Relation relation)
#ifdef RELCACHE_FORCE_RELEASE #ifdef RELCACHE_FORCE_RELEASE
if (RelationHasReferenceCountZero(relation) && if (RelationHasReferenceCountZero(relation) &&
!TransactionIdIsValid(relation->rd_createxact)) relation->rd_createSubid == InvalidSubTransactionId)
RelationClearRelation(relation, false); RelationClearRelation(relation, false);
#endif #endif
} }
...@@ -1736,7 +1736,7 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1736,7 +1736,7 @@ RelationClearRelation(Relation relation, bool rebuild)
{ {
/* /*
* When rebuilding an open relcache entry, must preserve ref count * When rebuilding an open relcache entry, must preserve ref count
* and rd_createxact state. Also attempt to preserve the * and rd_createSubid state. Also attempt to preserve the
* tupledesc and rewrite-rule substructures in place. * tupledesc and rewrite-rule substructures in place.
* *
* Note that this process does not touch CurrentResourceOwner; which * Note that this process does not touch CurrentResourceOwner; which
...@@ -1744,7 +1744,7 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1744,7 +1744,7 @@ RelationClearRelation(Relation relation, bool rebuild)
* necessarily belong to that resource owner. * necessarily belong to that resource owner.
*/ */
int old_refcnt = relation->rd_refcnt; int old_refcnt = relation->rd_refcnt;
TransactionId old_createxact = relation->rd_createxact; SubTransactionId old_createSubid = relation->rd_createSubid;
TupleDesc old_att = relation->rd_att; TupleDesc old_att = relation->rd_att;
RuleLock *old_rules = relation->rd_rules; RuleLock *old_rules = relation->rd_rules;
MemoryContext old_rulescxt = relation->rd_rulescxt; MemoryContext old_rulescxt = relation->rd_rulescxt;
...@@ -1765,7 +1765,7 @@ RelationClearRelation(Relation relation, bool rebuild) ...@@ -1765,7 +1765,7 @@ RelationClearRelation(Relation relation, bool rebuild)
buildinfo.i.info_id); buildinfo.i.info_id);
} }
relation->rd_refcnt = old_refcnt; relation->rd_refcnt = old_refcnt;
relation->rd_createxact = old_createxact; relation->rd_createSubid = old_createSubid;
if (equalTupleDescs(old_att, relation->rd_att)) if (equalTupleDescs(old_att, relation->rd_att))
{ {
/* needn't flush typcache here */ /* needn't flush typcache here */
...@@ -1802,7 +1802,7 @@ RelationFlushRelation(Relation relation) ...@@ -1802,7 +1802,7 @@ RelationFlushRelation(Relation relation)
{ {
bool rebuild; bool rebuild;
if (TransactionIdIsValid(relation->rd_createxact)) if (relation->rd_createSubid != InvalidSubTransactionId)
{ {
/* /*
* New relcache entries are always rebuilt, not flushed; else we'd * New relcache entries are always rebuilt, not flushed; else we'd
...@@ -1948,7 +1948,7 @@ RelationCacheInvalidate(void) ...@@ -1948,7 +1948,7 @@ RelationCacheInvalidate(void)
} }
/* Ignore new relations, since they are never SI targets */ /* Ignore new relations, since they are never SI targets */
if (TransactionIdIsValid(relation->rd_createxact)) if (relation->rd_createSubid != InvalidSubTransactionId)
continue; continue;
relcacheInvalsReceived++; relcacheInvalsReceived++;
...@@ -2032,10 +2032,10 @@ AtEOXact_RelationCache(bool isCommit) ...@@ -2032,10 +2032,10 @@ AtEOXact_RelationCache(bool isCommit)
* flush, the entry will get deleted anyway by shared-cache-inval * flush, the entry will get deleted anyway by shared-cache-inval
* processing of the aborted pg_class insertion.) * processing of the aborted pg_class insertion.)
*/ */
if (TransactionIdIsValid(relation->rd_createxact)) if (relation->rd_createSubid != InvalidSubTransactionId)
{ {
if (isCommit) if (isCommit)
relation->rd_createxact = InvalidTransactionId; relation->rd_createSubid = InvalidSubTransactionId;
else else
{ {
RelationClearRelation(relation, false); RelationClearRelation(relation, false);
...@@ -2097,8 +2097,8 @@ AtEOXact_RelationCache(bool isCommit) ...@@ -2097,8 +2097,8 @@ AtEOXact_RelationCache(bool isCommit)
* Note: this must be called *before* processing invalidation messages. * Note: this must be called *before* processing invalidation messages.
*/ */
void void
AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid, AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid) SubTransactionId parentSubid)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
RelIdCacheEnt *idhentry; RelIdCacheEnt *idhentry;
...@@ -2115,10 +2115,10 @@ AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid, ...@@ -2115,10 +2115,10 @@ AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid,
* During subcommit, mark it as belonging to the parent, instead. * During subcommit, mark it as belonging to the parent, instead.
* During subabort, simply delete the relcache entry. * During subabort, simply delete the relcache entry.
*/ */
if (TransactionIdEquals(relation->rd_createxact, myXid)) if (relation->rd_createSubid == mySubid)
{ {
if (isCommit) if (isCommit)
relation->rd_createxact = parentXid; relation->rd_createSubid = parentSubid;
else else
{ {
Assert(RelationHasReferenceCountZero(relation)); Assert(RelationHasReferenceCountZero(relation));
...@@ -2182,7 +2182,7 @@ RelationBuildLocalRelation(const char *relname, ...@@ -2182,7 +2182,7 @@ RelationBuildLocalRelation(const char *relname,
rel->rd_refcnt = nailit ? 1 : 0; rel->rd_refcnt = nailit ? 1 : 0;
/* it's being created in this transaction */ /* it's being created in this transaction */
rel->rd_createxact = GetCurrentTransactionId(); rel->rd_createSubid = GetCurrentSubTransactionId();
/* is it a temporary relation? */ /* is it a temporary relation? */
rel->rd_istemp = isTempNamespace(relnamespace); rel->rd_istemp = isTempNamespace(relnamespace);
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.71 2004/08/29 05:06:51 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.72 2004/09/16 16:58:36 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -192,7 +192,7 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent) ...@@ -192,7 +192,7 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent)
/* initialize portal fields that don't start off zero */ /* initialize portal fields that don't start off zero */
portal->cleanup = PortalCleanup; portal->cleanup = PortalCleanup;
portal->createXact = GetCurrentTransactionId(); portal->createSubid = GetCurrentSubTransactionId();
portal->strategy = PORTAL_MULTI_QUERY; portal->strategy = PORTAL_MULTI_QUERY;
portal->cursorOptions = CURSOR_OPT_NO_SCROLL; portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
portal->atStart = true; portal->atStart = true;
...@@ -427,7 +427,6 @@ AtCommit_Portals(void) ...@@ -427,7 +427,6 @@ AtCommit_Portals(void)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -450,12 +449,9 @@ AtCommit_Portals(void) ...@@ -450,12 +449,9 @@ AtCommit_Portals(void)
/* /*
* Do nothing else to cursors held over from a previous * Do nothing else to cursors held over from a previous
* transaction. (This test must include checking CURSOR_OPT_HOLD, * transaction.
* else we will fail to clean up a VACUUM portal if it fails after
* its first sub-transaction.)
*/ */
if (portal->createXact != xact && if (portal->createSubid == InvalidSubTransactionId)
(portal->cursorOptions & CURSOR_OPT_HOLD))
continue; continue;
if ((portal->cursorOptions & CURSOR_OPT_HOLD) && if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
...@@ -479,6 +475,12 @@ AtCommit_Portals(void) ...@@ -479,6 +475,12 @@ AtCommit_Portals(void)
* longer have its own resources. * longer have its own resources.
*/ */
portal->resowner = NULL; portal->resowner = NULL;
/*
* Having successfully exported the holdable cursor, mark it
* as not belonging to this transaction.
*/
portal->createSubid = InvalidSubTransactionId;
} }
else else
{ {
...@@ -502,7 +504,6 @@ AtAbort_Portals(void) ...@@ -502,7 +504,6 @@ AtAbort_Portals(void)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -515,12 +516,9 @@ AtAbort_Portals(void) ...@@ -515,12 +516,9 @@ AtAbort_Portals(void)
/* /*
* Do nothing else to cursors held over from a previous * Do nothing else to cursors held over from a previous
* transaction. (This test must include checking CURSOR_OPT_HOLD, * transaction.
* else we will fail to clean up a VACUUM portal if it fails after
* its first sub-transaction.)
*/ */
if (portal->createXact != xact && if (portal->createSubid == InvalidSubTransactionId)
(portal->cursorOptions & CURSOR_OPT_HOLD))
continue; continue;
/* let portalcmds.c clean up the state it knows about */ /* let portalcmds.c clean up the state it knows about */
...@@ -548,7 +546,6 @@ AtCleanup_Portals(void) ...@@ -548,7 +546,6 @@ AtCleanup_Portals(void)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId xact = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -556,14 +553,8 @@ AtCleanup_Portals(void) ...@@ -556,14 +553,8 @@ AtCleanup_Portals(void)
{ {
Portal portal = hentry->portal; Portal portal = hentry->portal;
/* /* Do nothing to cursors held over from a previous transaction */
* Do nothing else to cursors held over from a previous if (portal->createSubid == InvalidSubTransactionId)
* transaction. (This test must include checking CURSOR_OPT_HOLD,
* else we will fail to clean up a VACUUM portal if it fails after
* its first sub-transaction.)
*/
if (portal->createXact != xact &&
(portal->cursorOptions & CURSOR_OPT_HOLD))
{ {
Assert(portal->status != PORTAL_ACTIVE); Assert(portal->status != PORTAL_ACTIVE);
Assert(portal->resowner == NULL); Assert(portal->resowner == NULL);
...@@ -579,15 +570,15 @@ AtCleanup_Portals(void) ...@@ -579,15 +570,15 @@ AtCleanup_Portals(void)
* Pre-subcommit processing for portals. * Pre-subcommit processing for portals.
* *
* Reassign the portals created in the current subtransaction to the parent * Reassign the portals created in the current subtransaction to the parent
* transaction. * subtransaction.
*/ */
void void
AtSubCommit_Portals(TransactionId parentXid, AtSubCommit_Portals(SubTransactionId mySubid,
SubTransactionId parentSubid,
ResourceOwner parentXactOwner) ResourceOwner parentXactOwner)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId curXid = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -595,9 +586,9 @@ AtSubCommit_Portals(TransactionId parentXid, ...@@ -595,9 +586,9 @@ AtSubCommit_Portals(TransactionId parentXid,
{ {
Portal portal = hentry->portal; Portal portal = hentry->portal;
if (portal->createXact == curXid) if (portal->createSubid == mySubid)
{ {
portal->createXact = parentXid; portal->createSubid = parentSubid;
if (portal->resowner) if (portal->resowner)
ResourceOwnerNewParent(portal->resowner, parentXactOwner); ResourceOwnerNewParent(portal->resowner, parentXactOwner);
} }
...@@ -612,12 +603,12 @@ AtSubCommit_Portals(TransactionId parentXid, ...@@ -612,12 +603,12 @@ AtSubCommit_Portals(TransactionId parentXid,
* in descendants of the subtransaction too. * in descendants of the subtransaction too.
*/ */
void void
AtSubAbort_Portals(TransactionId parentXid, AtSubAbort_Portals(SubTransactionId mySubid,
SubTransactionId parentSubid,
ResourceOwner parentXactOwner) ResourceOwner parentXactOwner)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId curXid = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -625,7 +616,7 @@ AtSubAbort_Portals(TransactionId parentXid, ...@@ -625,7 +616,7 @@ AtSubAbort_Portals(TransactionId parentXid,
{ {
Portal portal = hentry->portal; Portal portal = hentry->portal;
if (portal->createXact != curXid) if (portal->createSubid != mySubid)
continue; continue;
/* /*
...@@ -644,7 +635,7 @@ AtSubAbort_Portals(TransactionId parentXid, ...@@ -644,7 +635,7 @@ AtSubAbort_Portals(TransactionId parentXid,
*/ */
if (portal->status == PORTAL_READY) if (portal->status == PORTAL_READY)
{ {
portal->createXact = parentXid; portal->createSubid = parentSubid;
if (portal->resowner) if (portal->resowner)
ResourceOwnerNewParent(portal->resowner, parentXactOwner); ResourceOwnerNewParent(portal->resowner, parentXactOwner);
} }
...@@ -674,11 +665,10 @@ AtSubAbort_Portals(TransactionId parentXid, ...@@ -674,11 +665,10 @@ AtSubAbort_Portals(TransactionId parentXid,
* we will not drop any that were reassigned to the parent above). * we will not drop any that were reassigned to the parent above).
*/ */
void void
AtSubCleanup_Portals(void) AtSubCleanup_Portals(SubTransactionId mySubid)
{ {
HASH_SEQ_STATUS status; HASH_SEQ_STATUS status;
PortalHashEnt *hentry; PortalHashEnt *hentry;
TransactionId curXid = GetCurrentTransactionId();
hash_seq_init(&status, PortalHashTable); hash_seq_init(&status, PortalHashTable);
...@@ -686,7 +676,7 @@ AtSubCleanup_Portals(void) ...@@ -686,7 +676,7 @@ AtSubCleanup_Portals(void)
{ {
Portal portal = hentry->portal; Portal portal = hentry->portal;
if (portal->createXact != curXid) if (portal->createSubid != mySubid)
continue; continue;
/* AtSubAbort_Portals should have fixed these: */ /* AtSubAbort_Portals should have fixed these: */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.72 2004/09/05 23:01:26 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.73 2004/09/16 16:58:37 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,14 +46,21 @@ extern bool XactReadOnly; ...@@ -46,14 +46,21 @@ extern bool XactReadOnly;
*/ */
typedef enum typedef enum
{ {
XACT_EVENT_ABORT,
XACT_EVENT_COMMIT, XACT_EVENT_COMMIT,
XACT_EVENT_START_SUB, XACT_EVENT_ABORT
XACT_EVENT_ABORT_SUB,
XACT_EVENT_COMMIT_SUB
} XactEvent; } XactEvent;
typedef void (*XactCallback) (XactEvent event, TransactionId parentXid, void *arg); typedef void (*XactCallback) (XactEvent event, void *arg);
typedef enum
{
SUBXACT_EVENT_START_SUB,
SUBXACT_EVENT_COMMIT_SUB,
SUBXACT_EVENT_ABORT_SUB
} SubXactEvent;
typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg);
/* ---------------- /* ----------------
...@@ -101,6 +108,8 @@ extern bool IsTransactionState(void); ...@@ -101,6 +108,8 @@ extern bool IsTransactionState(void);
extern bool IsAbortedTransactionBlockState(void); extern bool IsAbortedTransactionBlockState(void);
extern TransactionId GetTopTransactionId(void); extern TransactionId GetTopTransactionId(void);
extern TransactionId GetCurrentTransactionId(void); extern TransactionId GetCurrentTransactionId(void);
extern TransactionId GetCurrentTransactionIdIfAny(void);
extern SubTransactionId GetCurrentSubTransactionId(void);
extern CommandId GetCurrentCommandId(void); extern CommandId GetCurrentCommandId(void);
extern AbsoluteTime GetCurrentTransactionStartTime(void); extern AbsoluteTime GetCurrentTransactionStartTime(void);
extern AbsoluteTime GetCurrentTransactionStartTimeUsec(int *usec); extern AbsoluteTime GetCurrentTransactionStartTimeUsec(int *usec);
...@@ -129,6 +138,8 @@ extern void RequireTransactionChain(void *stmtNode, const char *stmtType); ...@@ -129,6 +138,8 @@ extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
extern bool IsInTransactionChain(void *stmtNode); extern bool IsInTransactionChain(void *stmtNode);
extern void RegisterXactCallback(XactCallback callback, void *arg); extern void RegisterXactCallback(XactCallback callback, void *arg);
extern void UnregisterXactCallback(XactCallback callback, void *arg); extern void UnregisterXactCallback(XactCallback callback, void *arg);
extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
extern void UnregisterSubXactCallback(SubXactCallback callback, void *arg);
extern void RecordTransactionCommit(void); extern void RecordTransactionCommit(void);
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/c.h,v 1.171 2004/09/10 15:51:47 momjian Exp $ * $PostgreSQL: pgsql/src/include/c.h,v 1.172 2004/09/16 16:58:38 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -351,7 +351,7 @@ typedef float float4; ...@@ -351,7 +351,7 @@ typedef float float4;
typedef double float8; typedef double float8;
/* /*
* Oid, RegProcedure, TransactionId, CommandId, AclId * Oid, RegProcedure, TransactionId, SubTransactionId, CommandId, AclId
*/ */
/* typedef Oid is in postgres_ext.h */ /* typedef Oid is in postgres_ext.h */
...@@ -365,6 +365,11 @@ typedef regproc RegProcedure; ...@@ -365,6 +365,11 @@ typedef regproc RegProcedure;
typedef uint32 TransactionId; typedef uint32 TransactionId;
typedef uint32 SubTransactionId;
#define InvalidSubTransactionId ((SubTransactionId) 0)
#define TopSubTransactionId ((SubTransactionId) 1)
typedef uint32 CommandId; typedef uint32 CommandId;
#define FirstCommandId ((CommandId) 0) #define FirstCommandId ((CommandId) 0)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.33 2004/08/29 05:06:55 momjian Exp $ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.34 2004/09/16 16:58:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -91,8 +91,8 @@ extern Oid FindDefaultConversionProc(int4 for_encoding, int4 to_encoding); ...@@ -91,8 +91,8 @@ extern Oid FindDefaultConversionProc(int4 for_encoding, int4 to_encoding);
/* initialization & transaction cleanup code */ /* initialization & transaction cleanup code */
extern void InitializeSearchPath(void); extern void InitializeSearchPath(void);
extern void AtEOXact_Namespace(bool isCommit); extern void AtEOXact_Namespace(bool isCommit);
extern void AtEOSubXact_Namespace(bool isCommit, TransactionId myXid, extern void AtEOSubXact_Namespace(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid); SubTransactionId parentSubid);
/* stuff for search_path GUC variable */ /* stuff for search_path GUC variable */
extern char *namespace_search_path; extern char *namespace_search_path;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.19 2004/08/29 05:06:56 momjian Exp $ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.20 2004/09/16 16:58:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,9 +42,9 @@ extern void register_on_commit_action(Oid relid, OnCommitAction action); ...@@ -42,9 +42,9 @@ extern void register_on_commit_action(Oid relid, OnCommitAction action);
extern void remove_on_commit_action(Oid relid); extern void remove_on_commit_action(Oid relid);
extern void PreCommit_on_commit_actions(void); extern void PreCommit_on_commit_actions(void);
extern void AtEOXact_on_commit_actions(bool isCommit, TransactionId xid); extern void AtEOXact_on_commit_actions(bool isCommit);
extern void AtEOSubXact_on_commit_actions(bool isCommit, extern void AtEOSubXact_on_commit_actions(bool isCommit,
TransactionId childXid, SubTransactionId mySubid,
TransactionId parentXid); SubTransactionId parentSubid);
#endif /* TABLECMDS_H */ #endif /* TABLECMDS_H */
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Commands for manipulating users and groups. * Commands for manipulating users and groups.
* *
* *
* $PostgreSQL: pgsql/src/include/commands/user.h,v 1.24 2004/08/29 05:06:56 momjian Exp $ * $PostgreSQL: pgsql/src/include/commands/user.h,v 1.25 2004/09/16 16:58:39 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,7 +32,8 @@ extern void RenameGroup(const char *oldname, const char *newname); ...@@ -32,7 +32,8 @@ extern void RenameGroup(const char *oldname, const char *newname);
extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS); extern Datum update_pg_pwd_and_pg_group(PG_FUNCTION_ARGS);
extern void AtEOXact_UpdatePasswordFile(bool isCommit); extern void AtEOXact_UpdatePasswordFile(bool isCommit);
extern void AtEOSubXact_UpdatePasswordFile(bool isCommit, TransactionId myXid, extern void AtEOSubXact_UpdatePasswordFile(bool isCommit,
TransactionId parentXid); SubTransactionId mySubid,
SubTransactionId parentSubid);
#endif /* USER_H */ #endif /* USER_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* *
* spi.h * spi.h
* *
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.48 2004/09/13 20:07:53 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.49 2004/09/16 16:58:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -126,6 +126,6 @@ extern void SPI_cursor_move(Portal portal, bool forward, int count); ...@@ -126,6 +126,6 @@ extern void SPI_cursor_move(Portal portal, bool forward, int count);
extern void SPI_cursor_close(Portal portal); extern void SPI_cursor_close(Portal portal);
extern void AtEOXact_SPI(bool isCommit); extern void AtEOXact_SPI(bool isCommit);
extern void AtEOSubXact_SPI(bool isCommit, TransactionId childXid); extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
#endif /* SPI_H */ #endif /* SPI_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.20 2004/08/29 04:13:07 momjian Exp $ * $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.21 2004/09/16 16:58:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,7 +23,7 @@ typedef struct ...@@ -23,7 +23,7 @@ typedef struct
MemoryContext procCxt; /* procedure context */ MemoryContext procCxt; /* procedure context */
MemoryContext execCxt; /* executor context */ MemoryContext execCxt; /* executor context */
MemoryContext savedcxt; MemoryContext savedcxt;
TransactionId connectXid; /* Xid of connecting transaction */ SubTransactionId connectSubid; /* ID of connecting subtransaction */
} _SPI_connection; } _SPI_connection;
typedef struct typedef struct
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.21 2004/08/29 05:06:56 momjian Exp $ * $PostgreSQL: pgsql/src/include/libpq/be-fsstubs.h,v 1.22 2004/09/16 16:58:41 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,7 +46,7 @@ extern int lo_write(int fd, char *buf, int len); ...@@ -46,7 +46,7 @@ extern int lo_write(int fd, char *buf, int len);
* Cleanup LOs at xact commit/abort * Cleanup LOs at xact commit/abort
*/ */
extern void AtEOXact_LargeObject(bool isCommit); extern void AtEOXact_LargeObject(bool isCommit);
extern void AtEOSubXact_LargeObject(bool isCommit, TransactionId myXid, extern void AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid); SubTransactionId parentSubid);
#endif /* BE_FSSTUBS_H */ #endif /* BE_FSSTUBS_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.48 2004/08/29 05:06:58 momjian Exp $ * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.49 2004/09/16 16:58:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -85,8 +85,8 @@ extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode); ...@@ -85,8 +85,8 @@ extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode);
extern void set_max_safe_fds(void); extern void set_max_safe_fds(void);
extern void closeAllVfds(void); extern void closeAllVfds(void);
extern void AtEOXact_Files(void); extern void AtEOXact_Files(void);
extern void AtEOSubXact_Files(bool isCommit, TransactionId myXid, extern void AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid); SubTransactionId parentSubid);
extern void RemovePgTempFiles(void); extern void RemovePgTempFiles(void);
extern int pg_fsync(int fd); extern int pg_fsync(int fd);
extern int pg_fdatasync(int fd); extern int pg_fdatasync(int fd);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.29 2004/08/29 04:13:10 momjian Exp $ * $PostgreSQL: pgsql/src/include/storage/large_object.h,v 1.30 2004/09/16 16:58:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Data about a currently-open large object. * Data about a currently-open large object.
* *
* id is the logical OID of the large object * id is the logical OID of the large object
* xid is the transaction Id that opened the LO (or currently owns it) * subid is the subtransaction that opened the LO (or currently owns it)
* offset is the current seek offset within the LO * offset is the current seek offset within the LO
* flags contains some flag bits * flags contains some flag bits
* *
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
typedef struct LargeObjectDesc typedef struct LargeObjectDesc
{ {
Oid id; /* LO's identifier */ Oid id; /* LO's identifier */
TransactionId xid; /* owning XID */ SubTransactionId subid; /* owning subtransaction ID */
uint32 offset; /* current seek pointer */ uint32 offset; /* current seek pointer */
int flags; /* locking info, etc */ int flags; /* locking info, etc */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.43 2004/08/29 04:13:10 momjian Exp $ * $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.44 2004/09/16 16:58:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -58,6 +58,7 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); ...@@ -58,6 +58,7 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
/* Lock an XID (used to wait for a transaction to finish) */ /* Lock an XID (used to wait for a transaction to finish) */
extern void XactLockTableInsert(TransactionId xid); extern void XactLockTableInsert(TransactionId xid);
extern void XactLockTableDelete(TransactionId xid);
extern void XactLockTableWait(TransactionId xid); extern void XactLockTableWait(TransactionId xid);
#endif /* LMGR_H */ #endif /* LMGR_H */
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.52 2004/08/29 05:06:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.53 2004/09/16 16:58:43 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -106,7 +106,11 @@ typedef struct PortalData ...@@ -106,7 +106,11 @@ typedef struct PortalData
MemoryContext heap; /* subsidiary memory for portal */ MemoryContext heap; /* subsidiary memory for portal */
ResourceOwner resowner; /* resources owned by portal */ ResourceOwner resowner; /* resources owned by portal */
void (*cleanup) (Portal portal); /* cleanup hook */ void (*cleanup) (Portal portal); /* cleanup hook */
TransactionId createXact; /* the xid of the creating xact */ SubTransactionId createSubid; /* the ID of the creating subxact */
/*
* if createSubid is InvalidSubTransactionId, the portal is held over
* from a previous transaction
*/
/* The query or queries the portal will execute */ /* The query or queries the portal will execute */
const char *sourceText; /* text of query, if known (may be NULL) */ const char *sourceText; /* text of query, if known (may be NULL) */
...@@ -181,11 +185,13 @@ extern void EnablePortalManager(void); ...@@ -181,11 +185,13 @@ extern void EnablePortalManager(void);
extern void AtCommit_Portals(void); extern void AtCommit_Portals(void);
extern void AtAbort_Portals(void); extern void AtAbort_Portals(void);
extern void AtCleanup_Portals(void); extern void AtCleanup_Portals(void);
extern void AtSubCommit_Portals(TransactionId parentXid, extern void AtSubCommit_Portals(SubTransactionId mySubid,
SubTransactionId parentSubid,
ResourceOwner parentXactOwner); ResourceOwner parentXactOwner);
extern void AtSubAbort_Portals(TransactionId parentXid, extern void AtSubAbort_Portals(SubTransactionId mySubid,
SubTransactionId parentSubid,
ResourceOwner parentXactOwner); ResourceOwner parentXactOwner);
extern void AtSubCleanup_Portals(void); extern void AtSubCleanup_Portals(SubTransactionId mySubid);
extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent); extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
extern Portal CreateNewPortal(void); extern Portal CreateNewPortal(void);
extern void PortalDrop(Portal portal, bool isTopCommit); extern void PortalDrop(Portal portal, bool isTopCommit);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.79 2004/08/29 05:06:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.80 2004/09/16 16:58:43 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -115,12 +115,12 @@ typedef struct RelationData ...@@ -115,12 +115,12 @@ typedef struct RelationData
bool rd_isvalid; /* relcache entry is valid */ bool rd_isvalid; /* relcache entry is valid */
char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1 char rd_indexvalid; /* state of rd_indexlist: 0 = not valid, 1
* = valid, 2 = temporarily forced */ * = valid, 2 = temporarily forced */
TransactionId rd_createxact; /* rel was created in current xact */ SubTransactionId rd_createSubid; /* rel was created in current xact */
/* /*
* rd_createxact is the XID of the highest subtransaction the rel has * rd_createSubid is the ID of the highest subtransaction the rel has
* survived into; or zero if the rel was not created in the current * survived into; or zero if the rel was not created in the current
* transaction. This should be relied on only for optimization * top transaction. This should be relied on only for optimization
* purposes; it is possible for new-ness to be "forgotten" (eg, after * purposes; it is possible for new-ness to be "forgotten" (eg, after
* CLUSTER). * CLUSTER).
*/ */
...@@ -241,7 +241,8 @@ typedef Relation *RelationPtr; ...@@ -241,7 +241,8 @@ typedef Relation *RelationPtr;
* Beware of multiple eval of argument * Beware of multiple eval of argument
*/ */
#define RELATION_IS_LOCAL(relation) \ #define RELATION_IS_LOCAL(relation) \
((relation)->rd_istemp || TransactionIdIsValid((relation)->rd_createxact)) ((relation)->rd_istemp || \
(relation)->rd_createSubid != InvalidSubTransactionId)
/* routines in utils/cache/relcache.c */ /* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel); extern void RelationIncrementReferenceCount(Relation rel);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.45 2004/08/29 05:06:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/relcache.h,v 1.46 2004/09/16 16:58:43 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -66,8 +66,8 @@ extern void RelationCacheInvalidateEntry(Oid relationId, RelFileNode *rnode); ...@@ -66,8 +66,8 @@ extern void RelationCacheInvalidateEntry(Oid relationId, RelFileNode *rnode);
extern void RelationCacheInvalidate(void); extern void RelationCacheInvalidate(void);
extern void AtEOXact_RelationCache(bool isCommit); extern void AtEOXact_RelationCache(bool isCommit);
extern void AtEOSubXact_RelationCache(bool isCommit, TransactionId myXid, extern void AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
TransactionId parentXid); SubTransactionId parentSubid);
/* /*
* Routines to help manage rebuilding of relcache init file * Routines to help manage rebuilding of relcache init file
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.119 2004/09/13 20:09:20 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.120 2004/09/16 16:58:44 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -4240,34 +4240,22 @@ exec_set_found(PLpgSQL_execstate *estate, bool state) ...@@ -4240,34 +4240,22 @@ exec_set_found(PLpgSQL_execstate *estate, bool state)
} }
/* /*
* plpgsql_eoxact --- post-transaction-commit-or-abort cleanup * plpgsql_xact_cb --- post-transaction-commit-or-abort cleanup
* *
* If a simple_eval_estate was created in the current transaction, * If a simple_eval_estate was created in the current transaction,
* it has to be cleaned up, and we have to mark all active PLpgSQL_expr * it has to be cleaned up, and we have to mark all active PLpgSQL_expr
* structs that are using it as no longer active. * structs that are using it as no longer active.
*
* XXX Do we need to do anything at subtransaction events?
* Maybe subtransactions need to have their own simple_eval_estate?
* It would get a lot messier, so for now let's assume we don't need that.
*/ */
void void
plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg) plpgsql_xact_cb(XactEvent event, void *arg)
{ {
PLpgSQL_expr *expr; PLpgSQL_expr *expr;
PLpgSQL_expr *enext; PLpgSQL_expr *enext;
switch (event)
{
/*
* Nothing to do at subtransaction events
*
* XXX really? Maybe subtransactions need to have their own
* simple_eval_estate? It would get a lot messier, so for now
* let's assume we don't need that.
*/
case XACT_EVENT_START_SUB:
case XACT_EVENT_ABORT_SUB:
case XACT_EVENT_COMMIT_SUB:
break;
case XACT_EVENT_ABORT:
case XACT_EVENT_COMMIT:
/* Mark all active exprs as inactive */ /* Mark all active exprs as inactive */
for (expr = active_simple_exprs; expr; expr = enext) for (expr = active_simple_exprs; expr; expr = enext)
{ {
...@@ -4286,6 +4274,4 @@ plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg) ...@@ -4286,6 +4274,4 @@ plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg)
if (event == XACT_EVENT_COMMIT && simple_eval_estate) if (event == XACT_EVENT_COMMIT && simple_eval_estate)
FreeExecutorState(simple_eval_estate); FreeExecutorState(simple_eval_estate);
simple_eval_estate = NULL; simple_eval_estate = NULL;
break;
}
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.55 2004/09/14 23:46:46 neilc Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.56 2004/09/16 16:58:44 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -708,7 +708,7 @@ extern Datum plpgsql_exec_function(PLpgSQL_function *func, ...@@ -708,7 +708,7 @@ extern Datum plpgsql_exec_function(PLpgSQL_function *func,
FunctionCallInfo fcinfo); FunctionCallInfo fcinfo);
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func, extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
TriggerData *trigdata); TriggerData *trigdata);
extern void plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg); extern void plpgsql_xact_cb(XactEvent event, void *arg);
/* ---------- /* ----------
* Functions for the dynamic string handling in pl_funcs.c * Functions for the dynamic string handling in pl_funcs.c
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment