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;