@ -1,4 +1,4 @@
$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
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
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 (f or example, in DefineRelation it is
operations in the same utility command. (F or 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
sequence:
sequence:
/ StartTransactionCommand;
/ StartTransactionCommand;
/ ProcessUtility; << BEGIN
/ StartTransaction;
1) < BeginTransactionBlock;
1) < ProcessUtility; << BEGIN
\ CommitTransactionCommand ;
\ BeginTransactionBlock ;
\ StartTransaction ;
\ CommitTransactionCommand ;
/ 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
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_END ABORT
* 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:
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
internal ly. In particular, we mustn't destroy TopTransactionContext until
complete ly. 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.
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
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 w e
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,