Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b6197fe

Browse files
committed
Further review of xact.c state machine for nested transactions. Fix
problems with starting subtransactions inside already-failed transactions. Clean up some comments.
1 parent e15d0bb commit b6197fe

File tree

3 files changed

+175
-32
lines changed

3 files changed

+175
-32
lines changed

src/backend/access/transam/xact.c

Lines changed: 84 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.169 2004/07/01 00:49:42 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.170 2004/07/01 20:11:02 tgl Exp $
1212
*
1313
* NOTES
1414
* Transaction aborts can now occur two ways:
@@ -196,6 +196,7 @@ static void StartSubTransaction(void);
196196
static void CommitSubTransaction(void);
197197
static void AbortSubTransaction(void);
198198
static void CleanupSubTransaction(void);
199+
static void StartAbortedSubTransaction(void);
199200
static void PushTransaction(void);
200201
static void PopTransaction(void);
201202

@@ -317,7 +318,7 @@ IsAbortedTransactionBlockState(void)
317318
TransactionState s = CurrentTransactionState;
318319

319320
if (s->blockState == TBLOCK_ABORT ||
320-
s->blockState == TBLOCK_SUBABORT)
321+
s->blockState == TBLOCK_SUBABORT)
321322
return true;
322323

323324
return false;
@@ -1579,10 +1580,9 @@ StartTransactionCommand(void)
15791580
break;
15801581

15811582
/*
1582-
* This is the case when are somewhere in a transaction block
1583+
* This is the case when we are somewhere in a transaction block
15831584
* and about to start a new command. For now we do nothing
1584-
* but someday we may do command-local resource
1585-
* initialization.
1585+
* but someday we may do command-local resource initialization.
15861586
*/
15871587
case TBLOCK_INPROGRESS:
15881588
case TBLOCK_SUBINPROGRESS:
@@ -1699,7 +1699,9 @@ CommitTransactionCommand(void)
16991699

17001700
/*
17011701
* We were just issued a BEGIN inside a transaction block.
1702-
* Start a subtransaction.
1702+
* Start a subtransaction. (BeginTransactionBlock already
1703+
* did PushTransaction, so as to have someplace to put the
1704+
* SUBBEGIN state.)
17031705
*/
17041706
case TBLOCK_SUBBEGIN:
17051707
StartSubTransaction();
@@ -1711,8 +1713,7 @@ CommitTransactionCommand(void)
17111713
* Start a subtransaction, and put it in aborted state.
17121714
*/
17131715
case TBLOCK_SUBBEGINABORT:
1714-
StartSubTransaction();
1715-
AbortSubTransaction();
1716+
StartAbortedSubTransaction();
17161717
s->blockState = TBLOCK_SUBABORT;
17171718
break;
17181719

@@ -1724,7 +1725,7 @@ CommitTransactionCommand(void)
17241725
break;
17251726

17261727
/*
1727-
* We where issued a COMMIT command, so we end the current
1728+
* We were issued a COMMIT command, so we end the current
17281729
* subtransaction and return to the parent transaction.
17291730
*/
17301731
case TBLOCK_SUBEND:
@@ -1740,7 +1741,7 @@ CommitTransactionCommand(void)
17401741
break;
17411742

17421743
/*
1743-
* We are ending a subtransaction that aborted nicely,
1744+
* We are ending an aborted subtransaction via ROLLBACK,
17441745
* so the parent can be allowed to live.
17451746
*/
17461747
case TBLOCK_SUBENDABORT_OK:
@@ -1750,9 +1751,8 @@ CommitTransactionCommand(void)
17501751
break;
17511752

17521753
/*
1753-
* We are ending a subtransaction that aborted in a unclean
1754-
* way (e.g. the user issued COMMIT in an aborted subtrasaction.)
1755-
* Abort the subtransaction, and abort the parent too.
1754+
* We are ending an aborted subtransaction via COMMIT.
1755+
* End the subtransaction, and abort the parent too.
17561756
*/
17571757
case TBLOCK_SUBENDABORT_ERROR:
17581758
CleanupSubTransaction();
@@ -1791,7 +1791,7 @@ AbortCurrentTransaction(void)
17911791
break;
17921792

17931793
/*
1794-
* If we are in the TBLOCK_BEGIN it means something screwed up
1794+
* If we are in TBLOCK_BEGIN it means something screwed up
17951795
* right after reading "BEGIN TRANSACTION" so we enter the
17961796
* abort state. Eventually an "END TRANSACTION" will fix
17971797
* things.
@@ -1803,10 +1803,10 @@ AbortCurrentTransaction(void)
18031803
break;
18041804

18051805
/*
1806-
* This is the case when are somewhere in a transaction block
1807-
* which aborted so we abort the transaction and set the ABORT
1808-
* state. Eventually an "END TRANSACTION" will fix things and
1809-
* restore us to a normal state.
1806+
* This is the case when we are somewhere in a transaction block
1807+
* and we've gotten a failure, so we abort the transaction and
1808+
* set up the persistent ABORT state. We will stay in ABORT
1809+
* until we get an "END TRANSACTION".
18101810
*/
18111811
case TBLOCK_INPROGRESS:
18121812
AbortTransaction();
@@ -1817,7 +1817,7 @@ AbortCurrentTransaction(void)
18171817
/*
18181818
* Here, the system was fouled up just after the user wanted
18191819
* to end the transaction block so we abort the transaction
1820-
* and put us back into the default state.
1820+
* and return to the default state.
18211821
*/
18221822
case TBLOCK_END:
18231823
AbortTransaction();
@@ -1852,10 +1852,7 @@ AbortCurrentTransaction(void)
18521852
*/
18531853
case TBLOCK_SUBBEGIN:
18541854
case TBLOCK_SUBBEGINABORT:
1855-
PushTransaction();
1856-
s = CurrentTransactionState; /* changed by push */
1857-
StartSubTransaction();
1858-
AbortSubTransaction();
1855+
StartAbortedSubTransaction();
18591856
s->blockState = TBLOCK_SUBABORT;
18601857
break;
18611858

@@ -2092,8 +2089,10 @@ CallEOXactCallbacks(bool isCommit)
20922089
* transaction block support
20932090
* ----------------------------------------------------------------
20942091
*/
2092+
20952093
/*
20962094
* BeginTransactionBlock
2095+
* This executes a BEGIN command.
20972096
*/
20982097
void
20992098
BeginTransactionBlock(void)
@@ -2102,7 +2101,7 @@ BeginTransactionBlock(void)
21022101

21032102
switch (s->blockState) {
21042103
/*
2105-
* We are inside a transaction, so allow a transaction block
2104+
* We are not inside a transaction block, so allow one
21062105
* to begin.
21072106
*/
21082107
case TBLOCK_STARTED:
@@ -2149,6 +2148,7 @@ BeginTransactionBlock(void)
21492148

21502149
/*
21512150
* EndTransactionBlock
2151+
* This executes a COMMIT command.
21522152
*/
21532153
void
21542154
EndTransactionBlock(void)
@@ -2176,9 +2176,9 @@ EndTransactionBlock(void)
21762176
break;
21772177

21782178
/*
2179-
* here, we are in a transaction block which aborted and since the
2180-
* AbortTransaction() was already done, we do whatever is needed
2181-
* and change to the special "END ABORT" state. The upcoming
2179+
* here, we are in a transaction block which aborted. Since the
2180+
* AbortTransaction() was already done, we need only
2181+
* change to the special "END ABORT" state. The upcoming
21822182
* CommitTransactionCommand() will recognise this and then put us
21832183
* back in the default state.
21842184
*/
@@ -2189,7 +2189,8 @@ EndTransactionBlock(void)
21892189
/*
21902190
* here we are in an aborted subtransaction. Signal
21912191
* CommitTransactionCommand() to clean up and return to the
2192-
* parent transaction.
2192+
* parent transaction. Since the user said COMMIT, we must
2193+
* fail the parent transaction.
21932194
*/
21942195
case TBLOCK_SUBABORT:
21952196
s->blockState = TBLOCK_SUBENDABORT_ERROR;
@@ -2209,7 +2210,7 @@ EndTransactionBlock(void)
22092210
s->blockState = TBLOCK_ENDABORT;
22102211
break;
22112212

2212-
/* These cases are invalid. Reject them altogether. */
2213+
/* these cases are invalid. */
22132214
case TBLOCK_DEFAULT:
22142215
case TBLOCK_BEGIN:
22152216
case TBLOCK_ENDABORT:
@@ -2227,6 +2228,7 @@ EndTransactionBlock(void)
22272228

22282229
/*
22292230
* UserAbortTransactionBlock
2231+
* This executes a ROLLBACK command.
22302232
*/
22312233
void
22322234
UserAbortTransactionBlock(void)
@@ -2244,7 +2246,10 @@ UserAbortTransactionBlock(void)
22442246
s->blockState = TBLOCK_ENDABORT;
22452247
break;
22462248

2247-
/* Ditto, for a subtransaction. */
2249+
/*
2250+
* Ditto, for a subtransaction. Here it is okay to allow the
2251+
* parent transaction to continue.
2252+
*/
22482253
case TBLOCK_SUBABORT:
22492254
s->blockState = TBLOCK_SUBENDABORT_OK;
22502255
break;
@@ -2336,8 +2341,8 @@ AbortOutOfAnyTransaction(void)
23362341
case TBLOCK_SUBBEGIN:
23372342
case TBLOCK_SUBBEGINABORT:
23382343
/*
2339-
* Just starting a new transaction -- return to parent.
2340-
* FIXME -- Is this correct?
2344+
* We didn't get as far as starting the subxact, so there's
2345+
* nothing to abort. Just pop back to parent.
23412346
*/
23422347
PopTransaction();
23432348
s = CurrentTransactionState; /* changed by pop */
@@ -2353,6 +2358,7 @@ AbortOutOfAnyTransaction(void)
23532358
case TBLOCK_SUBABORT:
23542359
case TBLOCK_SUBENDABORT_OK:
23552360
case TBLOCK_SUBENDABORT_ERROR:
2361+
/* As above, but AbortSubTransaction already done */
23562362
CleanupSubTransaction();
23572363
PopTransaction();
23582364
s = CurrentTransactionState; /* changed by pop */
@@ -2521,6 +2527,8 @@ CommitSubTransaction(void)
25212527
AtSubCommit_Portals(s->parent->transactionIdData);
25222528
DeferredTriggerEndSubXact(true);
25232529

2530+
s->state = TRANS_COMMIT;
2531+
25242532
/* Mark subtransaction as subcommitted */
25252533
CommandCounterIncrement();
25262534
RecordSubTransactionCommit();
@@ -2642,6 +2650,49 @@ CleanupSubTransaction(void)
26422650
s->state = TRANS_DEFAULT;
26432651
}
26442652

2653+
/*
2654+
* StartAbortedSubTransaction
2655+
*
2656+
* This function is used to start a subtransaction and put it immediately
2657+
* into aborted state. The end result should be equivalent to
2658+
* StartSubTransaction immediately followed by AbortSubTransaction.
2659+
* The reason we don't implement it just that way is that many of the backend
2660+
* modules aren't designed to handle starting a subtransaction when not
2661+
* inside a valid transaction. Rather than making them all capable of
2662+
* doing that, we just omit the paired start and abort calls in this path.
2663+
*/
2664+
static void
2665+
StartAbortedSubTransaction(void)
2666+
{
2667+
TransactionState s = CurrentTransactionState;
2668+
2669+
if (s->state != TRANS_DEFAULT)
2670+
elog(WARNING, "StartAbortedSubTransaction and not in default state");
2671+
2672+
s->state = TRANS_START;
2673+
2674+
/*
2675+
* We don't bother to generate a new Xid, so the end state is not
2676+
* *exactly* like we had done a full Start/AbortSubTransaction...
2677+
*/
2678+
s->transactionIdData = InvalidTransactionId;
2679+
2680+
/* Make sure currentUser is reasonably valid */
2681+
Assert(s->parent != NULL);
2682+
s->currentUser = s->parent->currentUser;
2683+
2684+
/*
2685+
* Initialize only what has to be there for CleanupSubTransaction to work.
2686+
*/
2687+
AtSubStart_Memory();
2688+
2689+
s->state = TRANS_ABORT;
2690+
2691+
AtSubAbort_Memory();
2692+
2693+
ShowTransactionState("StartAbortedSubTransaction");
2694+
}
2695+
26452696
/*
26462697
* PushTransaction
26472698
* Set up transaction state for a subtransaction
@@ -2672,6 +2723,7 @@ PushTransaction(void)
26722723
*/
26732724
s->transactionIdData = p->transactionIdData;
26742725
s->curTransactionContext = p->curTransactionContext;
2726+
s->currentUser = p->currentUser;
26752727

26762728
CurrentTransactionState = s;
26772729
}

src/test/regress/expected/transactions.out

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,65 @@ SELECT * FROM barbaz; -- should have 1
132132
1
133133
(1 row)
134134

135+
-- check that starting a subxact in a failed xact or subxact works
136+
BEGIN;
137+
SELECT 0/0; -- fail the outer xact
138+
ERROR: division by zero
139+
BEGIN;
140+
SELECT 1; -- this should NOT work
141+
ERROR: current transaction is aborted, commands ignored until end of transaction block
142+
COMMIT;
143+
SELECT 1; -- this should NOT work
144+
ERROR: current transaction is aborted, commands ignored until end of transaction block
145+
BEGIN;
146+
SELECT 1; -- this should NOT work
147+
ERROR: current transaction is aborted, commands ignored until end of transaction block
148+
ROLLBACK;
149+
SELECT 1; -- this should NOT work
150+
ERROR: current transaction is aborted, commands ignored until end of transaction block
151+
COMMIT;
152+
SELECT 1; -- this should work
153+
?column?
154+
----------
155+
1
156+
(1 row)
157+
158+
BEGIN;
159+
BEGIN;
160+
SELECT 1; -- this should work
161+
?column?
162+
----------
163+
1
164+
(1 row)
165+
166+
SELECT 0/0; -- fail the subxact
167+
ERROR: division by zero
168+
SELECT 1; -- this should NOT work
169+
ERROR: current transaction is aborted, commands ignored until end of transaction block
170+
BEGIN;
171+
SELECT 1; -- this should NOT work
172+
ERROR: current transaction is aborted, commands ignored until end of transaction block
173+
ROLLBACK;
174+
BEGIN;
175+
SELECT 1; -- this should NOT work
176+
ERROR: current transaction is aborted, commands ignored until end of transaction block
177+
COMMIT;
178+
SELECT 1; -- this should NOT work
179+
ERROR: current transaction is aborted, commands ignored until end of transaction block
180+
ROLLBACK;
181+
SELECT 1; -- this should work
182+
?column?
183+
----------
184+
1
185+
(1 row)
186+
187+
COMMIT;
188+
SELECT 1; -- this should work
189+
?column?
190+
----------
191+
1
192+
(1 row)
193+
135194
DROP TABLE foo;
136195
DROP TABLE baz;
137196
DROP TABLE barbaz;

0 commit comments

Comments
 (0)