Hackers,
Here is a patch covering the syntax change. This changes the current
subtransaction-initiating command to SUBBEGIN instead of BEGIN;
similarly SUBCOMMIT and SUBABORT.
I did not add a SUBROLLBACK command ... rather I want to use the
standard syntax "SAVEPOINT <foo>" and "ROLLBACK TO <foo>" and keep our
nonstandard syntax small.
Note that with this patch it is possible to start a subtransaction when
not in a transaction block. This makes the new commands to behave
almost exactly like toplevel BEGIN/COMMIT (the difference is not visible
to the user: the server is in nesting level 2 rather than 1, but the
outer level will automatically commit or roll back).
This also means that a single COMMIT will commit the whole transaction
tree:
BEGIN;
create table foo (a int);
SUBBEGIN;
insert into foo values (1);
SUBBEGIN;
insert into foo values (2);
COMMIT;
Also a single ABORT/ROLLBACK aborts the whole thing.
Included in this patch is the ability to "ignore errors" in a subcommit,
so this works:
begin;
subbegin;
drop table foo;
-- error: table does not exist
subcommit ignore errors;
create table foo (...);
commit;
The point is that this can be executed in a dumb script without worrying
about whether the subtransaction will cause an error or not.
I'm not sure if the grammar modifications are good. I thought about
using two Sconst and comparing them to "ignore errors" ... right now,
"ignore" and "errors" are in the unreserved keywords list and I get no
errors/warnings from bison, but please check this.
I had to add a new transaction block state to support rolling back a
whole transaction tree. Also I moved the TransactionState declaration
to xact.c because it has no business being in the xact.h header file
that I can see.
I made some changes to SPI so that it forbids to close a subtransaction
that the _SPI_connection did not open, by saving the nesting level at
SPI_connect() time and checking when SPI_execute is called. I had
thought that it would be easy to return to that nesting level if the
function errored out, but I was quite wrong (because the SPI code stops
executing immediately as soon as an error is encountered). Now I don't
know how to do that at all. I also thought about adding something to
the sigsetjmp() block but I don't have a clue how to handle this.
Regression tests pass; I had to change the transaction test to adopt the
new syntax. I also added a couple of tests to verify the new
functionality.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"La experiencia nos dice que el hombre pel� millones de veces las patatas,
pero era forzoso admitir la posibilidad de que en un caso entre millones,
las patatas pelar�an al hombre" (Ijon Tichy)
Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/xact.c,v
retrieving revision 1.170
diff -c -r1.170 xact.c
*** src/backend/access/transam/xact.c 1 Jul 2004 20:11:02 -0000 1.170
--- src/backend/access/transam/xact.c 5 Jul 2004 03:40:48 -0000
***************
*** 173,215 ****
#include "pgstat.h"
! static void AbortTransaction(void);
! static void AtAbort_Cache(void);
! static void AtAbort_Locks(void);
! static void AtAbort_Memory(void);
! static void AtCleanup_Memory(void);
! static void AtCommit_Cache(void);
! static void AtCommit_LocalCache(void);
! static void AtCommit_Locks(void);
! static void AtCommit_Memory(void);
! static void AtStart_Cache(void);
! static void AtStart_Locks(void);
! static void AtStart_Memory(void);
! static void CallEOXactCallbacks(bool isCommit);
! 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 void AtSubAbort_Locks(void);
! static void AtSubAbort_Memory(void);
! static void AtSubCleanup_Memory(void);
! static void AtSubCommit_Memory(void);
! static void AtSubStart_Memory(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
--- 173,234 ----
#include "pgstat.h"
! /*
! * transaction states - transaction state from server perspective
! */
! typedef enum TransState
! {
! TRANS_DEFAULT,
! TRANS_START,
! TRANS_INPROGRESS,
! TRANS_COMMIT,
! TRANS_ABORT
! } TransState;
!
! /*
! * transaction block states - transaction state of client queries
! */
! typedef enum TBlockState
! {
! /* not-in-transaction-block states */
! TBLOCK_DEFAULT,
! TBLOCK_STARTED,
!
! /* transaction block states */
! TBLOCK_BEGIN,
! TBLOCK_INPROGRESS,
! TBLOCK_END,
! TBLOCK_ABORT,
! TBLOCK_ENDABORT,
!
! /* subtransaction states */
! TBLOCK_SUBBEGIN,
! TBLOCK_SUBBEGINABORT,
! TBLOCK_SUBINPROGRESS,
! TBLOCK_SUBEND,
! TBLOCK_SUBABORT,
! TBLOCK_SUBENDABORT_ALL,
! TBLOCK_SUBENDABORT_OK,
! TBLOCK_SUBENDABORT_ERROR
! } TBlockState;
!
! /*
! * transaction state structure
! */
! typedef struct TransactionStateData
! {
! TransactionId transactionIdData; /* my XID */
! CommandId commandId; /* current CID
*/
! TransState state; /* low-level
state */
! TBlockState blockState; /* high-level
state */
! int nestingLevel; /* nest depth
*/
! MemoryContext curTransactionContext; /* my xact-lifetime context */
! List *childXids; /* subcommitted child
XIDs */
! AclId currentUser; /* subxact start
current_user */
! struct TransactionStateData *parent; /* back link to parent */
! } TransactionStateData;
! typedef TransactionStateData *TransactionState;
/*
* CurrentTransactionState always points to the current transaction state
***************
*** 270,275 ****
--- 289,333 ----
static void *_RollbackData = NULL;
+ /* private functions declarations */
+ static void AbortTransaction(void);
+ static void AtAbort_Cache(void);
+ static void AtAbort_Locks(void);
+ static void AtAbort_Memory(void);
+ static void AtCleanup_Memory(void);
+ static void AtCommit_Cache(void);
+ static void AtCommit_LocalCache(void);
+ static void AtCommit_Locks(void);
+ static void AtCommit_Memory(void);
+ static void AtStart_Cache(void);
+ static void AtStart_Locks(void);
+ static void AtStart_Memory(void);
+ static void CallEOXactCallbacks(bool isCommit);
+ 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 void AtSubAbort_Locks(void);
+ static void AtSubAbort_Memory(void);
+ static void AtSubCleanup_Memory(void);
+ static void AtSubCommit_Memory(void);
+ static void AtSubStart_Memory(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
* ----------------------------------------------------------------
***************
*** 1605,1610 ****
--- 1663,1669 ----
case TBLOCK_SUBBEGINABORT:
case TBLOCK_END:
case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
case TBLOCK_ENDABORT:
***************
*** 1631,1641 ****
switch (s->blockState)
{
! /*
! * This shouldn't happen, because it means the previous
! * StartTransactionCommand didn't set the STARTED state
! * appropiately.
! */
case TBLOCK_DEFAULT:
elog(FATAL, "CommitTransactionCommand: unexpected
TBLOCK_DEFAULT");
break;
--- 1690,1700 ----
switch (s->blockState)
{
! /*
! * This shouldn't happen, because it means the previous
! * StartTransactionCommand didn't set the STARTED state
! * appropiately.
! */
case TBLOCK_DEFAULT:
elog(FATAL, "CommitTransactionCommand: unexpected
TBLOCK_DEFAULT");
break;
***************
*** 1674,1679 ****
--- 1733,1744 ----
* default state.
*/
case TBLOCK_END:
+ while (GetCurrentTransactionNestLevel() > 1)
+ {
+ CommitSubTransaction();
+ PopTransaction();
+ s = CurrentTransactionState; /* changed by
pop */
+ }
CommitTransaction();
s->blockState = TBLOCK_DEFAULT;
break;
***************
*** 1698,1704 ****
break;
/*
! * We were just issued a BEGIN inside a transaction block.
* Start a subtransaction. (BeginTransactionBlock already
* did PushTransaction, so as to have someplace to put the
* SUBBEGIN state.)
--- 1763,1778 ----
break;
/*
! * Ditto, but in a subtransaction. AbortOutOfAnyTransaction
! * will do the dirty work.
! */
! case TBLOCK_SUBENDABORT_ALL:
! AbortOutOfAnyTransaction();
! /* AbortOutOfAnyTransaction sets the blockState */
! break;
!
! /*
! * We were just issued a SUBBEGIN inside a transaction block.
* Start a subtransaction. (BeginTransactionBlock already
* did PushTransaction, so as to have someplace to put the
* SUBBEGIN state.)
***************
*** 1709,1715 ****
break;
/*
! * We were issued a BEGIN inside an aborted transaction block.
* Start a subtransaction, and put it in aborted state.
*/
case TBLOCK_SUBBEGINABORT:
--- 1783,1789 ----
break;
/*
! * We were issued a SUBBEGIN inside an aborted transaction
block.
* Start a subtransaction, and put it in aborted state.
*/
case TBLOCK_SUBBEGINABORT:
***************
*** 1725,1731 ****
break;
/*
! * We were issued a COMMIT command, so we end the current
* subtransaction and return to the parent transaction.
*/
case TBLOCK_SUBEND:
--- 1799,1805 ----
break;
/*
! * We were issued a SUBCOMMIT command, so we end the current
* subtransaction and return to the parent transaction.
*/
case TBLOCK_SUBEND:
***************
*** 1741,1747 ****
break;
/*
! * We are ending an aborted subtransaction via ROLLBACK,
* so the parent can be allowed to live.
*/
case TBLOCK_SUBENDABORT_OK:
--- 1815,1821 ----
break;
/*
! * We are ending an aborted subtransaction via SUBABORT,
* so the parent can be allowed to live.
*/
case TBLOCK_SUBENDABORT_OK:
***************
*** 1751,1764 ****
break;
/*
! * We are ending an aborted subtransaction via COMMIT.
! * End the subtransaction, and abort the parent too.
*/
case TBLOCK_SUBENDABORT_ERROR:
CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */
! Assert(s->blockState != TBLOCK_SUBENDABORT_ERROR);
AbortCurrentTransaction();
break;
}
--- 1825,1839 ----
break;
/*
! * We are ending an aborted subtransaction via SUBCOMMIT
! * without IGNORE ERRORS. End the subtransaction, and
! * abort the parent too.
*/
case TBLOCK_SUBENDABORT_ERROR:
CleanupSubTransaction();
PopTransaction();
s = CurrentTransactionState; /* changed by pop */
! AssertState(s->blockState != TBLOCK_SUBENDABORT_ERROR);
AbortCurrentTransaction();
break;
}
***************
*** 1889,1894 ****
--- 1964,1977 ----
s->blockState != TBLOCK_SUBENDABORT_ERROR);
AbortCurrentTransaction();
break;
+
+ /*
+ * We are already aborting the whole transaction tree.
+ * Do nothing, CommitTransactionCommand will call
+ * AbortOutOfAnyTransaction and set things straight.
+ */
+ case TBLOCK_SUBENDABORT_ALL:
+ break;
}
}
***************
*** 2091,2096 ****
--- 2174,2227 ----
*/
/*
+ * BeginSubTransactionBlock
+ * This executes a SUBBEGIN command.
+ */
+ void
+ BeginSubTransactionBlock(void)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_STARTED:
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_SUBINPROGRESS:
+ /* Normal subtransaction start */
+ PushTransaction();
+ s = CurrentTransactionState; /* changed by push */
+ s->blockState = TBLOCK_SUBBEGIN;
+ break;
+
+ case TBLOCK_ABORT:
+ case TBLOCK_SUBABORT:
+ /*
+ * An aborted transaction block should be allowed to start
+ * a subtransaction, but it must put it in aborted state.
+ */
+ PushTransaction();
+ s = CurrentTransactionState; /* changed by push */
+ s->blockState = TBLOCK_SUBBEGINABORT;
+ break;
+
+ /* These cases are invalid. Reject them altogether. */
+ case TBLOCK_DEFAULT:
+ case TBLOCK_BEGIN:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_SUBBEGINABORT:
+ case TBLOCK_ENDABORT:
+ case TBLOCK_END:
+ case TBLOCK_SUBENDABORT_ALL:
+ case TBLOCK_SUBENDABORT_OK:
+ case TBLOCK_SUBENDABORT_ERROR:
+ case TBLOCK_SUBEND:
+ elog(FATAL, "BeginTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState));
+ break;
+ }
+ }
+
+ /*
* BeginTransactionBlock
* This executes a BEGIN command.
*/
***************
*** 2099,2105 ****
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState) {
/*
* We are not inside a transaction block, so allow one
* to begin.
--- 2230,2237 ----
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState)
! {
/*
* We are not inside a transaction block, so allow one
* to begin.
***************
*** 2110,2133 ****
/*
* Already a transaction block in progress.
- * Start a subtransaction.
*/
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
- PushTransaction();
- s = CurrentTransactionState; /* changed by push */
- s->blockState = TBLOCK_SUBBEGIN;
- break;
-
- /*
- * An aborted transaction block should be allowed to start
- * a subtransaction, but it must put it in aborted state.
- */
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
! PushTransaction();
! s = CurrentTransactionState; /* changed by push */
! s->blockState = TBLOCK_SUBBEGINABORT;
break;
/* These cases are invalid. Reject them altogether. */
--- 2242,2255 ----
/*
* Already a transaction block in progress.
*/
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
! ereport(WARNING,
! (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
! errmsg("there is already a transaction in
progress")));
break;
/* These cases are invalid. Reject them altogether. */
***************
*** 2137,2142 ****
--- 2259,2265 ----
case TBLOCK_SUBBEGINABORT:
case TBLOCK_ENDABORT:
case TBLOCK_END:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
case TBLOCK_SUBEND:
***************
*** 2147,2152 ****
--- 2270,2348 ----
}
/*
+ * EndSubTransactionBlock
+ * This executes a SUBEND command.
+ */
+ void
+ EndSubTransactionBlock(List *options)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_ABORT:
+ case TBLOCK_STARTED:
+ /* XXX is this the right ERRCODE? */
+ ereport(WARNING,
+ (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
+ errmsg("there is no subtransaction in
progress")));
+ break;
+
+ /*
+ * here we are in a subtransaction block. Signal
+ * CommitTransactionCommand() to end it and return to the
+ * parent transaction.
+ */
+ case TBLOCK_SUBINPROGRESS:
+ s->blockState = TBLOCK_SUBEND;
+ break;
+
+ /*
+ * here we are in an aborted subtransaction. Signal
+ * CommitTransactionCommand() to clean up and return to the
+ * parent transaction. If the user didn't specify to ignore
+ * the errors then make the parent abort too; else it can be
+ * allowed to live.
+ */
+ case TBLOCK_SUBABORT:
+ {
+ ListCell *cell;
+ bool ignore = false;
+
+ foreach (cell, options)
+ {
+ DefElem *elem = lfirst(cell);
+
+ if (strcmp(elem->defname, "ignore_errors") ==
0)
+ ignore = true;
+ }
+
+ if (ignore)
+ s->blockState = TBLOCK_SUBENDABORT_OK;
+ else
+ s->blockState = TBLOCK_SUBENDABORT_ERROR;
+ }
+ break;
+
+ /* these cases are invalid. */
+ case TBLOCK_DEFAULT:
+ case TBLOCK_BEGIN:
+ case TBLOCK_ENDABORT:
+ case TBLOCK_END:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_SUBBEGINABORT:
+ case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
+ case TBLOCK_SUBENDABORT_OK:
+ case TBLOCK_SUBENDABORT_ERROR:
+ elog(FATAL, "EndTransactionBlock: unexpected state %s",
+ BlockStateAsString(s->blockState));
+ break;
+ }
+ }
+
+ /*
* EndTransactionBlock
* This executes a COMMIT command.
*/
***************
*** 2155,2161 ****
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState) {
/*
* here we are in a transaction block which should commit when we
* get to the upcoming CommitTransactionCommand() so we set the
--- 2351,2358 ----
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState)
! {
/*
* here we are in a transaction block which should commit when we
* get to the upcoming CommitTransactionCommand() so we set the
***************
*** 2163,2178 ****
* and commit the transaction and return us to the default state
*/
case TBLOCK_INPROGRESS:
- s->blockState = TBLOCK_END;
- break;
-
- /*
- * here we are in a subtransaction block. Signal
- * CommitTransactionCommand() to end it and return to the
- * parent transaction.
- */
case TBLOCK_SUBINPROGRESS:
! s->blockState = TBLOCK_SUBEND;
break;
/*
--- 2360,2367 ----
* and commit the transaction and return us to the default state
*/
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
! s->blockState = TBLOCK_END;
break;
/*
***************
*** 2187,2199 ****
break;
/*
! * here we are in an aborted subtransaction. Signal
! * CommitTransactionCommand() to clean up and return to the
! * parent transaction. Since the user said COMMIT, we must
! * fail the parent transaction.
*/
case TBLOCK_SUBABORT:
! s->blockState = TBLOCK_SUBENDABORT_ERROR;
break;
case TBLOCK_STARTED:
--- 2376,2387 ----
break;
/*
! * Ditto, but in a subtransaction. Go to the "abort the whole
! * tree" state so that CommitTransactionCommand calls
! * AbortOutOfAnyTransaction.
*/
case TBLOCK_SUBABORT:
! s->blockState = TBLOCK_SUBENDABORT_ALL;
break;
case TBLOCK_STARTED:
***************
*** 2218,2223 ****
--- 2406,2412 ----
case TBLOCK_SUBBEGIN:
case TBLOCK_SUBBEGINABORT:
case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
elog(FATAL, "EndTransactionBlock: unexpected state %s",
***************
*** 2227,2232 ****
--- 2416,2470 ----
}
/*
+ * UserAbortSubTransactionBlock
+ * This executes a SUBABORT command.
+ */
+ void
+ UserAbortSubTransactionBlock(void)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_ABORT:
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_STARTED:
+ /*
+ * XXX is this the correct ERRCODE?
+ * Is an ERROR ok or should it be WARNING?
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
+ errmsg("there is no subtransaction in
progress")));
+ break;
+
+ case TBLOCK_SUBABORT:
+ s->blockState = TBLOCK_SUBENDABORT_OK;
+ break;
+
+ case TBLOCK_SUBINPROGRESS:
+ AbortSubTransaction();
+ s->blockState = TBLOCK_SUBENDABORT_OK;
+ break;
+
+ /* these cases are invalid. */
+ case TBLOCK_DEFAULT:
+ case TBLOCK_BEGIN:
+ case TBLOCK_END:
+ case TBLOCK_ENDABORT:
+ case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
+ case TBLOCK_SUBENDABORT_OK:
+ case TBLOCK_SUBENDABORT_ERROR:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_SUBBEGINABORT:
+ elog(FATAL, "UserAbortSubTransactionBlock: unexpected state
%s",
+ BlockStateAsString(s->blockState));
+ break;
+ }
+ }
+
+ /*
* UserAbortTransactionBlock
* This executes a ROLLBACK command.
*/
***************
*** 2235,2241 ****
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState) {
/*
* here we are inside a failed transaction block and we got an abort
* command from the user. Abort processing is already done, we just
--- 2473,2480 ----
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState)
! {
/*
* here we are inside a failed transaction block and we got an abort
* command from the user. Abort processing is already done, we just
***************
*** 2246,2257 ****
s->blockState = TBLOCK_ENDABORT;
break;
! /*
! * Ditto, for a subtransaction. Here it is okay to allow the
! * parent transaction to continue.
*/
case TBLOCK_SUBABORT:
! s->blockState = TBLOCK_SUBENDABORT_OK;
break;
/*
--- 2485,2498 ----
s->blockState = TBLOCK_ENDABORT;
break;
! /* Here we are inside a failed subtransaction and we got
! * an abort command from the user. Abort processing is already
! * done, so go to the "abort all"
! * state and CommitTransactionCommand will call
! * AbortOutOfAnyTransaction to set things straight.
*/
case TBLOCK_SUBABORT:
! s->blockState = TBLOCK_SUBENDABORT_ALL;
break;
/*
***************
*** 2268,2274 ****
/* Ditto, for a subtransaction. */
case TBLOCK_SUBINPROGRESS:
AbortSubTransaction();
! s->blockState = TBLOCK_SUBENDABORT_OK;
break;
/*
--- 2509,2515 ----
/* Ditto, for a subtransaction. */
case TBLOCK_SUBINPROGRESS:
AbortSubTransaction();
! s->blockState = TBLOCK_SUBENDABORT_ALL;
break;
/*
***************
*** 2291,2296 ****
--- 2532,2538 ----
case TBLOCK_END:
case TBLOCK_ENDABORT:
case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
case TBLOCK_SUBBEGIN:
***************
*** 2299,2305 ****
BlockStateAsString(s->blockState));
break;
}
-
}
/*
--- 2541,2546 ----
***************
*** 2356,2361 ****
--- 2597,2603 ----
s = CurrentTransactionState; /* changed by
pop */
break;
case TBLOCK_SUBABORT:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
/* As above, but AbortSubTransaction already done */
***************
*** 2425,2430 ****
--- 2667,2673 ----
case TBLOCK_ABORT:
case TBLOCK_ENDABORT:
case TBLOCK_SUBABORT:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
case TBLOCK_SUBBEGINABORT:
***************
*** 2445,2451 ****
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState) {
case TBLOCK_DEFAULT:
case TBLOCK_STARTED:
case TBLOCK_BEGIN:
--- 2688,2695 ----
{
TransactionState s = CurrentTransactionState;
! switch (s->blockState)
! {
case TBLOCK_DEFAULT:
case TBLOCK_STARTED:
case TBLOCK_BEGIN:
***************
*** 2459,2464 ****
--- 2703,2709 ----
case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
case TBLOCK_SUBEND:
+ case TBLOCK_SUBENDABORT_ALL:
case TBLOCK_SUBENDABORT_OK:
case TBLOCK_SUBENDABORT_ERROR:
return true;
***************
*** 2696,2701 ****
--- 2941,2949 ----
/*
* PushTransaction
* Set up transaction state for a subtransaction
+ *
+ * The caller has to make sure to always reassign CurrentTransactionState
+ * if it has a local pointer to it after calling this function.
*/
static void
PushTransaction(void)
***************
*** 2731,2736 ****
--- 2979,2987 ----
/*
* PopTransaction
* Pop back to parent transaction state
+ *
+ * The caller has to make sure to always reassign CurrentTransactionState
+ * if it has a local pointer to it after calling this function.
*/
static void
PopTransaction(void)
***************
*** 2799,2805 ****
static const char *
BlockStateAsString(TBlockState blockState)
{
! switch (blockState) {
case TBLOCK_DEFAULT:
return "DEFAULT";
case TBLOCK_STARTED:
--- 3050,3057 ----
static const char *
BlockStateAsString(TBlockState blockState)
{
! switch (blockState)
! {
case TBLOCK_DEFAULT:
return "DEFAULT";
case TBLOCK_STARTED:
***************
*** 2824,2829 ****
--- 3076,3083 ----
return "SUB END";
case TBLOCK_SUBABORT:
return "SUB ABORT";
+ case TBLOCK_SUBENDABORT_ALL:
+ return "SUB ENDAB ALL";
case TBLOCK_SUBENDABORT_OK:
return "SUB ENDAB OK";
case TBLOCK_SUBENDABORT_ERROR:
***************
*** 2839,2845 ****
static const char *
TransStateAsString(TransState state)
{
! switch (state) {
case TRANS_DEFAULT:
return "DEFAULT";
case TRANS_START:
--- 3093,3100 ----
static const char *
TransStateAsString(TransState state)
{
! switch (state)
! {
case TRANS_DEFAULT:
return "DEFAULT";
case TRANS_START:
Index: src/backend/executor/spi.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/executor/spi.c,v
retrieving revision 1.120
diff -c -r1.120 spi.c
*** src/backend/executor/spi.c 1 Jul 2004 21:17:13 -0000 1.120
--- src/backend/executor/spi.c 5 Jul 2004 03:43:50 -0000
***************
*** 103,108 ****
--- 103,109 ----
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
_SPI_current->connectXid = GetCurrentTransactionId();
+ _SPI_current->nestLevel = GetCurrentTransactionNestLevel();
/*
* Create memory contexts for this procedure
***************
*** 1181,1186 ****
--- 1182,1211 ----
res = SPI_ERROR_CURSOR;
goto fail;
}
+ else if (IsA(queryTree->utilityStmt, TransactionStmt))
+ {
+ TransactionStmt *XactStmt = (TransactionStmt
*)queryTree->utilityStmt;
+
+ if (XactStmt->kind != TRANS_STMT_SUBBEGIN &&
+ XactStmt->kind != TRANS_STMT_SUBCOMMIT
&&
+ XactStmt->kind != TRANS_STMT_SUBABORT)
+ {
+ res = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+
+ /*
+ * Disallow closing the transaction that
created the
+ * connection.
+ */
+ if ((XactStmt->kind == TRANS_STMT_SUBCOMMIT ||
+ XactStmt->kind ==
TRANS_STMT_SUBABORT) &&
+ _SPI_current->nestLevel >=
GetCurrentTransactionNestLevel())
+ {
+ res = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+ }
res = SPI_OK_UTILITY;
if (plan == NULL)
{
***************
*** 1306,1311 ****
--- 1331,1361 ----
dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None,
NULL);
if (queryTree->commandType == CMD_UTILITY)
{
+ if (IsA(queryTree->utilityStmt, TransactionStmt))
+ {
+ TransactionStmt *XactStmt = (TransactionStmt
*)queryTree->utilityStmt;
+
+ if (XactStmt->kind != TRANS_STMT_SUBBEGIN &&
+ XactStmt->kind != TRANS_STMT_SUBCOMMIT
&&
+ XactStmt->kind != TRANS_STMT_SUBABORT)
+ {
+ res = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+
+ /*
+ * Disallow closing the transaction that
created the
+ * connection.
+ */
+ if ((XactStmt->kind == TRANS_STMT_SUBCOMMIT ||
+ XactStmt->kind ==
TRANS_STMT_SUBABORT) &&
+ _SPI_current->nestLevel >=
GetCurrentTransactionNestLevel())
+ {
+ res = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+ }
+
ProcessUtility(queryTree->utilityStmt, dest, NULL);
res = SPI_OK_UTILITY;
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.465
diff -c -r2.465 gram.y
*** src/backend/parser/gram.y 28 Jun 2004 01:19:11 -0000 2.465
--- src/backend/parser/gram.y 4 Jul 2004 18:18:28 -0000
***************
*** 222,229 ****
target_list update_target_list insert_column_list
insert_target_list def_list indirection opt_indirection
group_clause TriggerFuncArgs select_limit
! opt_select_limit opclass_item_list
transaction_mode_list
! transaction_mode_list_or_empty
TableFuncElementList
prep_type_clause prep_type_list
execute_param_clause
--- 222,229 ----
target_list update_target_list insert_column_list
insert_target_list def_list indirection opt_indirection
group_clause TriggerFuncArgs select_limit
! opt_select_limit opclass_item_list
transaction_subcommit_opts
! transaction_mode_list transaction_mode_list_or_empty
TableFuncElementList
prep_type_clause prep_type_list
execute_param_clause
***************
*** 348,354 ****
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
! EACH ELSE ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
--- 348,354 ----
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
! EACH ELSE ENCODING ENCRYPTED END_P ERRORS ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
***************
*** 358,364 ****
HANDLER HAVING HOLD HOUR_P
! ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
INTERVAL INTO INVOKER IS ISNULL ISOLATION
--- 358,364 ----
HANDLER HAVING HOLD HOUR_P
! IGNORE ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
INTERVAL INTO INVOKER IS ISNULL ISOLATION
***************
*** 393,399 ****
SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
! STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SYSID
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
--- 393,400 ----
SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
! STATISTICS STDIN STDOUT STORAGE STRICT_P SUBABORT SUBBEGIN SUBCOMMIT
! SUBSTRING SYSID
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
***************
*** 3954,3959 ****
--- 3955,3981 ----
n->options = NIL;
$$ = (Node *)n;
}
+ | SUBBEGIN opt_transaction
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->kind = TRANS_STMT_SUBBEGIN;
+ n->options = NIL;
+ $$ = (Node *)n;
+ }
+ | SUBABORT opt_transaction
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->kind = TRANS_STMT_SUBABORT;
+ n->options = NIL;
+ $$ = (Node *)n;
+ }
+ | SUBCOMMIT opt_transaction transaction_subcommit_opts
+ {
+ TransactionStmt *n = makeNode(TransactionStmt);
+ n->kind = TRANS_STMT_SUBCOMMIT;
+ n->options = $3;
+ $$ = (Node *)n;
+ }
;
opt_transaction: WORK {}
***************
*** 3995,4000 ****
--- 4017,4031 ----
| READ WRITE { $$ = FALSE; }
;
+ transaction_subcommit_opts:
+ IGNORE ERRORS
+ {
+ $$ = list_make1(makeDefElem("ignore_errors",
+
makeBoolConst(true, false)));
+ }
+ | /* EMPTY */
+ { $$ = NIL; }
+ ;
/*****************************************************************************
*
***************
*** 7608,7613 ****
--- 7639,7645 ----
| EACH
| ENCODING
| ENCRYPTED
+ | ERRORS
| ESCAPE
| EXCLUDING
| EXCLUSIVE
***************
*** 7623,7628 ****
--- 7655,7661 ----
| HANDLER
| HOLD
| HOUR_P
+ | IGNORE
| IMMEDIATE
| IMMUTABLE
| IMPLICIT_P
***************
*** 7713,7718 ****
--- 7746,7754 ----
| STORAGE
| SYSID
| STRICT_P
+ | SUBABORT
+ | SUBBEGIN
+ | SUBCOMMIT
| TABLESPACE
| TEMP
| TEMPLATE
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/parser/keywords.c,v
retrieving revision 1.150
diff -c -r1.150 keywords.c
*** src/backend/parser/keywords.c 18 Jun 2004 06:13:31 -0000 1.150
--- src/backend/parser/keywords.c 4 Jul 2004 18:18:16 -0000
***************
*** 122,127 ****
--- 122,128 ----
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
+ {"errors", ERRORS},
{"escape", ESCAPE},
{"except", EXCEPT},
{"excluding", EXCLUDING},
***************
*** 150,155 ****
--- 151,157 ----
{"having", HAVING},
{"hold", HOLD},
{"hour", HOUR_P},
+ {"ignore", IGNORE},
{"ilike", ILIKE},
{"immediate", IMMEDIATE},
{"immutable", IMMUTABLE},
***************
*** 294,299 ****
--- 296,304 ----
{"stdout", STDOUT},
{"storage", STORAGE},
{"strict", STRICT_P},
+ {"subabort", SUBABORT},
+ {"subbegin", SUBBEGIN},
+ {"subcommit", SUBCOMMIT},
{"substring", SUBSTRING},
{"sysid", SYSID},
{"table", TABLE},
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.422
diff -c -r1.422 postgres.c
*** src/backend/tcop/postgres.c 1 Jul 2004 00:51:11 -0000 1.422
--- src/backend/tcop/postgres.c 4 Jul 2004 00:03:41 -0000
***************
*** 841,847 ****
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
allowit = true;
}
--- 841,849 ----
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_SUBBEGIN ||
! stmt->kind == TRANS_STMT_SUBCOMMIT ||
! stmt->kind == TRANS_STMT_SUBABORT ||
stmt->kind == TRANS_STMT_ROLLBACK)
allowit = true;
}
***************
*** 1162,1168 ****
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
allowit = true;
}
--- 1164,1172 ----
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_SUBBEGIN ||
! stmt->kind == TRANS_STMT_SUBCOMMIT ||
! stmt->kind == TRANS_STMT_SUBABORT ||
stmt->kind == TRANS_STMT_ROLLBACK)
allowit = true;
}
***************
*** 1625,1631 ****
is_trans_stmt = true;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_BEGIN ||
stmt->kind == TRANS_STMT_ROLLBACK)
is_trans_exit = true;
}
--- 1629,1637 ----
is_trans_stmt = true;
if (stmt->kind == TRANS_STMT_COMMIT ||
! stmt->kind == TRANS_STMT_SUBBEGIN ||
! stmt->kind == TRANS_STMT_SUBCOMMIT ||
! stmt->kind == TRANS_STMT_SUBABORT ||
stmt->kind == TRANS_STMT_ROLLBACK)
is_trans_exit = true;
}
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/utility.c,v
retrieving revision 1.220
diff -c -r1.220 utility.c
*** src/backend/tcop/utility.c 25 Jun 2004 21:55:57 -0000 1.220
--- src/backend/tcop/utility.c 4 Jul 2004 20:48:11 -0000
***************
*** 360,365 ****
--- 360,378 ----
case TRANS_STMT_ROLLBACK:
UserAbortTransactionBlock();
break;
+
+ case TRANS_STMT_SUBBEGIN:
+ BeginSubTransactionBlock();
+ break;
+
+ case TRANS_STMT_SUBABORT:
+ UserAbortSubTransactionBlock();
+ break;
+
+ /* Let EndSubTransactionBlock parse
the options list */
+ case TRANS_STMT_SUBCOMMIT:
+ EndSubTransactionBlock(stmt->options);
+ break;
}
}
break;
***************
*** 1117,1122 ****
--- 1130,1147 ----
tag = "ROLLBACK";
break;
+ case TRANS_STMT_SUBBEGIN:
+ tag = "SUBBEGIN";
+ break;
+
+ case TRANS_STMT_SUBABORT:
+ tag = "SUBABORT";
+ break;
+
+ case TRANS_STMT_SUBCOMMIT:
+ tag = "SUBCOMMIT";
+ break;
+
default:
tag = "???";
break;
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.66
diff -c -r1.66 portalmem.c
*** src/backend/utils/mmgr/portalmem.c 1 Jul 2004 00:51:29 -0000 1.66
--- src/backend/utils/mmgr/portalmem.c 4 Jul 2004 05:48:12 -0000
***************
*** 302,308 ****
/* Not sure if this case can validly happen or not... */
if (portal->portalActive)
! elog(ERROR, "cannot drop active portal");
/*
* Remove portal from hash table. Because we do this first, we will
--- 302,308 ----
/* Not sure if this case can validly happen or not... */
if (portal->portalActive)
! elog(FATAL, "cannot drop active portal");
/*
* Remove portal from hash table. Because we do this first, we will
Index: src/include/access/xact.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/xact.h,v
retrieving revision 1.64
diff -c -r1.64 xact.h
*** src/include/access/xact.h 1 Jul 2004 00:51:38 -0000 1.64
--- src/include/access/xact.h 5 Jul 2004 03:40:30 -0000
***************
*** 41,103 ****
extern bool XactReadOnly;
/*
- * transaction states - transaction state from server perspective
- */
- typedef enum TransState
- {
- TRANS_DEFAULT,
- TRANS_START,
- TRANS_INPROGRESS,
- TRANS_COMMIT,
- TRANS_ABORT
- } TransState;
-
- /*
- * transaction block states - transaction state of client queries
- */
- typedef enum TBlockState
- {
- TBLOCK_DEFAULT,
- TBLOCK_STARTED,
- TBLOCK_BEGIN,
- TBLOCK_INPROGRESS,
- TBLOCK_END,
- TBLOCK_ABORT,
- TBLOCK_ENDABORT,
-
- TBLOCK_SUBBEGIN,
- TBLOCK_SUBBEGINABORT,
- TBLOCK_SUBINPROGRESS,
- TBLOCK_SUBEND,
- TBLOCK_SUBABORT,
- TBLOCK_SUBENDABORT_OK,
- TBLOCK_SUBENDABORT_ERROR
- } TBlockState;
-
- /*
* end-of-transaction cleanup callbacks for dynamically loaded modules
*/
typedef void (*EOXactCallback) (bool isCommit, void *arg);
- /*
- * transaction state structure
- */
- typedef struct TransactionStateData
- {
- TransactionId transactionIdData; /* my XID */
- CommandId commandId; /* current CID
*/
- TransState state; /* low-level
state */
- TBlockState blockState; /* high-level
state */
- int nestingLevel; /* nest depth
*/
- MemoryContext curTransactionContext; /* my xact-lifetime context */
- List *childXids; /* subcommitted child
XIDs */
- AclId currentUser; /* subxact start
current_user */
- struct TransactionStateData *parent; /* back link to parent */
- } TransactionStateData;
-
- typedef TransactionStateData *TransactionState;
-
-
/* ----------------
* transaction-related XLOG entries
* ----------------
--- 41,50 ----
***************
*** 152,163 ****
--- 99,113 ----
extern void StartTransactionCommand(void);
extern void CommitTransactionCommand(void);
extern void AbortCurrentTransaction(void);
+ extern void BeginSubTransactionBlock(void);
extern void BeginTransactionBlock(void);
+ extern void EndSubTransactionBlock(List *options);
extern void EndTransactionBlock(void);
extern bool IsSubTransaction(void);
extern bool IsTransactionBlock(void);
extern bool IsTransactionOrTransactionBlock(void);
extern char TransactionBlockStatusCode(void);
+ extern void UserAbortSubTransactionBlock(void);
extern void UserAbortTransactionBlock(void);
extern void AbortOutOfAnyTransaction(void);
extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
Index: src/include/executor/spi_priv.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/executor/spi_priv.h,v
retrieving revision 1.19
diff -c -r1.19 spi_priv.h
*** src/include/executor/spi_priv.h 1 Jul 2004 00:51:42 -0000 1.19
--- src/include/executor/spi_priv.h 4 Jul 2004 21:40:25 -0000
***************
*** 24,29 ****
--- 24,30 ----
MemoryContext execCxt; /* executor context */
MemoryContext savedcxt;
TransactionId connectXid; /* Xid of connecting transaction */
+ int nestLevel; /* nesting level of connecting
transaction */
} _SPI_connection;
typedef struct
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/nodes/parsenodes.h,v
retrieving revision 1.260
diff -c -r1.260 parsenodes.h
*** src/include/nodes/parsenodes.h 25 Jun 2004 21:55:59 -0000 1.260
--- src/include/nodes/parsenodes.h 3 Jul 2004 21:02:18 -0000
***************
*** 1514,1527 ****
TRANS_STMT_BEGIN,
TRANS_STMT_START, /* semantically identical to BEGIN */
TRANS_STMT_COMMIT,
! TRANS_STMT_ROLLBACK
} TransactionStmtKind;
typedef struct TransactionStmt
{
NodeTag type;
TransactionStmtKind kind; /* see above */
! List *options; /* for BEGIN/START only */
} TransactionStmt;
/* ----------------------
--- 1514,1530 ----
TRANS_STMT_BEGIN,
TRANS_STMT_START, /* semantically identical to BEGIN */
TRANS_STMT_COMMIT,
! TRANS_STMT_ROLLBACK,
! TRANS_STMT_SUBBEGIN,
! TRANS_STMT_SUBCOMMIT,
! TRANS_STMT_SUBABORT
} TransactionStmtKind;
typedef struct TransactionStmt
{
NodeTag type;
TransactionStmtKind kind; /* see above */
! List *options; /* for BEGIN/START/SUBBEGIN only */
} TransactionStmt;
/* ----------------------
Index: src/test/regress/expected/transactions.out
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/test/regress/expected/transactions.out,v
retrieving revision 1.5
diff -c -r1.5 transactions.out
*** src/test/regress/expected/transactions.out 1 Jul 2004 20:11:02 -0000 1.5
--- src/test/regress/expected/transactions.out 4 Jul 2004 21:23:23 -0000
***************
*** 74,86 ****
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
! BEGIN;
DROP TABLE foo;
CREATE TABLE bar (a int);
! ROLLBACK;
! BEGIN;
CREATE TABLE baz (a int);
! COMMIT;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
--- 74,86 ----
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
! SUBBEGIN;
DROP TABLE foo;
CREATE TABLE bar (a int);
! SUBABORT;
! SUBBEGIN;
CREATE TABLE baz (a int);
! SUBCOMMIT;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
***************
*** 105,122 ****
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
! BEGIN;
INSERT into bar VALUES (1);
ERROR: relation "bar" does not exist
! ROLLBACK;
! BEGIN;
INSERT into barbaz VALUES (1);
! COMMIT;
! BEGIN;
! BEGIN;
INSERT INTO foo VALUES (2);
! COMMIT;
! ROLLBACK;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
--- 105,122 ----
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
! SUBBEGIN;
INSERT into bar VALUES (1);
ERROR: relation "bar" does not exist
! SUBABORT;
! SUBBEGIN;
INSERT into barbaz VALUES (1);
! SUBCOMMIT;
! SUBBEGIN;
! SUBBEGIN;
INSERT INTO foo VALUES (2);
! SUBCOMMIT;
! SUBABORT;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
***************
*** 136,151 ****
BEGIN;
SELECT 0/0; -- fail the outer xact
ERROR: division by zero
! BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! COMMIT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! ROLLBACK;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
COMMIT;
--- 136,151 ----
BEGIN;
SELECT 0/0; -- fail the outer xact
ERROR: division by zero
! SUBBEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBCOMMIT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBBEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBABORT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
COMMIT;
***************
*** 156,162 ****
(1 row)
BEGIN;
! BEGIN;
SELECT 1; -- this should work
?column?
----------
--- 156,162 ----
(1 row)
BEGIN;
! SUBBEGIN;
SELECT 1; -- this should work
?column?
----------
***************
*** 167,183 ****
ERROR: division by zero
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! ROLLBACK;
! BEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! COMMIT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! ROLLBACK;
SELECT 1; -- this should work
?column?
----------
--- 167,183 ----
ERROR: division by zero
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBBEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBABORT;
! SUBBEGIN;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBCOMMIT;
SELECT 1; -- this should NOT work
ERROR: current transaction is aborted, commands ignored until end of transaction
block
! SUBABORT;
SELECT 1; -- this should work
?column?
----------
***************
*** 191,196 ****
--- 191,255 ----
1
(1 row)
+ -- test "subcommit ignore errors" and whole-tree commit
+ BEGIN;
+ SUBBEGIN;
+ SELECT foo;
+ ERROR: column "foo" does not exist
+ SUBCOMMIT IGNORE ERRORS;
+ SUBBEGIN;
+ CREATE TABLE bazbaz (a int);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (1);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (2);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (3);
+ SUBABORT;
+ COMMIT;
+ COMMIT; -- should not be in a transaction block
+ WARNING: there is no transaction in progress
+ SELECT * FROM bazbaz;
+ a
+ ---
+ 1
+ 2
+ (2 rows)
+
+ -- test whole-tree rollback
+ BEGIN;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=1;
+ SUBCOMMIT;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=1;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=2;
+ ROLLBACK;
+
+ SELECT * FROM bazbaz;
+ a
+ ---
+ 1
+ 2
+ (2 rows)
+
+ -- test whole-tree commit on an aborted subtransaction
+ BEGIN;
+ INSERT INTO bazbaz VALUES (1);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (2);
+ SELECT foo;
+ ERROR: column "foo" does not exist
+ COMMIT;
+ SELECT * FROM bazbaz;
+ a
+ ---
+ 1
+ 2
+ (2 rows)
+
DROP TABLE foo;
DROP TABLE baz;
DROP TABLE barbaz;
+ DROP TABLE bazbaz;
Index: src/test/regress/sql/transactions.sql
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/test/regress/sql/transactions.sql,v
retrieving revision 1.5
diff -c -r1.5 transactions.sql
*** src/test/regress/sql/transactions.sql 1 Jul 2004 20:11:03 -0000 1.5
--- src/test/regress/sql/transactions.sql 4 Jul 2004 21:21:43 -0000
***************
*** 61,73 ****
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
! BEGIN;
DROP TABLE foo;
CREATE TABLE bar (a int);
! ROLLBACK;
! BEGIN;
CREATE TABLE baz (a int);
! COMMIT;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
--- 61,73 ----
CREATE TABLE foobar (a int);
BEGIN;
CREATE TABLE foo (a int);
! SUBBEGIN;
DROP TABLE foo;
CREATE TABLE bar (a int);
! SUBABORT;
! SUBBEGIN;
CREATE TABLE baz (a int);
! SUBCOMMIT;
drop TABLE foobar;
CREATE TABLE barbaz (a int);
COMMIT;
***************
*** 80,96 ****
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
! BEGIN;
INSERT into bar VALUES (1);
! ROLLBACK;
! BEGIN;
INSERT into barbaz VALUES (1);
! COMMIT;
! BEGIN;
! BEGIN;
INSERT INTO foo VALUES (2);
! COMMIT;
! ROLLBACK;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
--- 80,96 ----
-- inserts
BEGIN;
INSERT INTO foo VALUES (1);
! SUBBEGIN;
INSERT into bar VALUES (1);
! SUBABORT;
! SUBBEGIN;
INSERT into barbaz VALUES (1);
! SUBCOMMIT;
! SUBBEGIN;
! SUBBEGIN;
INSERT INTO foo VALUES (2);
! SUBCOMMIT;
! SUBABORT;
INSERT INTO foo VALUES (3);
COMMIT;
SELECT * FROM foo; -- should have 1 and 3
***************
*** 99,133 ****
-- check that starting a subxact in a failed xact or subxact works
BEGIN;
SELECT 0/0; -- fail the outer xact
! BEGIN;
SELECT 1; -- this should NOT work
! COMMIT;
SELECT 1; -- this should NOT work
! BEGIN;
SELECT 1; -- this should NOT work
! ROLLBACK;
SELECT 1; -- this should NOT work
COMMIT;
SELECT 1; -- this should work
BEGIN;
! BEGIN;
SELECT 1; -- this should work
SELECT 0/0; -- fail the subxact
SELECT 1; -- this should NOT work
! BEGIN;
SELECT 1; -- this should NOT work
! ROLLBACK;
! BEGIN;
SELECT 1; -- this should NOT work
! COMMIT;
SELECT 1; -- this should NOT work
! ROLLBACK;
SELECT 1; -- this should work
COMMIT;
SELECT 1; -- this should work
DROP TABLE foo;
DROP TABLE baz;
DROP TABLE barbaz;
--- 99,173 ----
-- check that starting a subxact in a failed xact or subxact works
BEGIN;
SELECT 0/0; -- fail the outer xact
! SUBBEGIN;
SELECT 1; -- this should NOT work
! SUBCOMMIT;
SELECT 1; -- this should NOT work
! SUBBEGIN;
SELECT 1; -- this should NOT work
! SUBABORT;
SELECT 1; -- this should NOT work
COMMIT;
SELECT 1; -- this should work
BEGIN;
! SUBBEGIN;
SELECT 1; -- this should work
SELECT 0/0; -- fail the subxact
SELECT 1; -- this should NOT work
! SUBBEGIN;
SELECT 1; -- this should NOT work
! SUBABORT;
! SUBBEGIN;
SELECT 1; -- this should NOT work
! SUBCOMMIT;
SELECT 1; -- this should NOT work
! SUBABORT;
SELECT 1; -- this should work
COMMIT;
SELECT 1; -- this should work
+ -- test "subcommit ignore errors" and whole-tree commit
+ BEGIN;
+ SUBBEGIN;
+ SELECT foo;
+ SUBCOMMIT IGNORE ERRORS;
+ SUBBEGIN;
+ CREATE TABLE bazbaz (a int);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (1);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (2);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (3);
+ SUBABORT;
+ COMMIT;
+ COMMIT; -- should not be in a transaction block
+ SELECT * FROM bazbaz;
+
+ -- test whole-tree rollback
+ BEGIN;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=1;
+ SUBCOMMIT;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=1;
+ SUBBEGIN;
+ DELETE FROM bazbaz WHERE a=2;
+ ROLLBACK;
+
+ SELECT * FROM bazbaz;
+
+ -- test whole-tree commit on an aborted subtransaction
+ BEGIN;
+ INSERT INTO bazbaz VALUES (1);
+ SUBBEGIN;
+ INSERT INTO bazbaz VALUES (2);
+ SELECT foo;
+ COMMIT;
+ SELECT * FROM bazbaz;
DROP TABLE foo;
DROP TABLE baz;
DROP TABLE barbaz;
+ DROP TABLE bazbaz;
---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend