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

Commit 083258e

Browse files
committed
Fix a number of places where brittle data structures or overly strong
Asserts would lead to a server core dump if an error occurred while trying to abort a failed subtransaction (thereby leading to re-execution of whatever parts of AbortSubTransaction had already run). This of course does not prevent such an error from creating an infinite loop, but at least we don't make the situation worse. Responds to an open item on the subtransactions to-do list.
1 parent d55588e commit 083258e

File tree

4 files changed

+101
-53
lines changed

4 files changed

+101
-53
lines changed

src/backend/commands/async.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.115 2004/08/29 05:06:41 momjian Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.116 2004/09/06 23:32:54 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -630,6 +630,9 @@ AtSubStart_Notify(void)
630630

631631
upperPendingNotifies = lcons(pendingNotifies, upperPendingNotifies);
632632

633+
Assert(list_length(upperPendingNotifies) ==
634+
GetCurrentTransactionNestLevel() - 1);
635+
633636
pendingNotifies = NIL;
634637

635638
MemoryContextSwitchTo(old_cxt);
@@ -648,6 +651,9 @@ AtSubCommit_Notify(void)
648651
parentPendingNotifies = (List *) linitial(upperPendingNotifies);
649652
upperPendingNotifies = list_delete_first(upperPendingNotifies);
650653

654+
Assert(list_length(upperPendingNotifies) ==
655+
GetCurrentTransactionNestLevel() - 2);
656+
651657
/*
652658
* We could try to eliminate duplicates here, but it seems not
653659
* worthwhile.
@@ -661,13 +667,23 @@ AtSubCommit_Notify(void)
661667
void
662668
AtSubAbort_Notify(void)
663669
{
670+
int my_level = GetCurrentTransactionNestLevel();
671+
664672
/*
665673
* All we have to do is pop the stack --- the notifies made in this
666674
* subxact are no longer interesting, and the space will be freed when
667675
* CurTransactionContext is recycled.
676+
*
677+
* This routine could be called more than once at a given nesting level
678+
* if there is trouble during subxact abort. Avoid dumping core by
679+
* using GetCurrentTransactionNestLevel as the indicator of how far
680+
* we need to prune the list.
668681
*/
669-
pendingNotifies = (List *) linitial(upperPendingNotifies);
670-
upperPendingNotifies = list_delete_first(upperPendingNotifies);
682+
while (list_length(upperPendingNotifies) > my_level - 2)
683+
{
684+
pendingNotifies = (List *) linitial(upperPendingNotifies);
685+
upperPendingNotifies = list_delete_first(upperPendingNotifies);
686+
}
671687
}
672688

673689
/*

src/backend/commands/trigger.c

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.168 2004/08/29 05:06:41 momjian Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.169 2004/09/06 23:32:54 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -1669,8 +1669,11 @@ ltrmark:;
16691669
* state data; each subtransaction level that modifies that state first
16701670
* saves a copy, which we use to restore the state if we abort.
16711671
*
1672-
* numpushed and numalloc keep control of allocation and storage in the above
1673-
* stacks. numpushed is essentially the current subtransaction nesting depth.
1672+
* We use GetCurrentTransactionNestLevel() to determine the correct array
1673+
* index in these stacks. numalloc is the number of allocated entries in
1674+
* each stack. (By not keeping our own stack pointer, we can avoid trouble
1675+
* in cases where errors during subxact abort cause multiple invocations
1676+
* of DeferredTriggerEndSubXact() at the same nesting depth.)
16741677
*
16751678
* XXX We need to be able to save the per-event data in a file if it grows too
16761679
* large.
@@ -1742,7 +1745,6 @@ typedef struct DeferredTriggersData
17421745
DeferredTriggerEvent *tail_stack;
17431746
DeferredTriggerEvent *imm_stack;
17441747
DeferredTriggerState *state_stack;
1745-
int numpushed;
17461748
int numalloc;
17471749
} DeferredTriggersData;
17481750

@@ -2165,7 +2167,6 @@ DeferredTriggerBeginXact(void)
21652167
deferredTriggers->imm_stack = NULL;
21662168
deferredTriggers->state_stack = NULL;
21672169
deferredTriggers->numalloc = 0;
2168-
deferredTriggers->numpushed = 0;
21692170
}
21702171

21712172

@@ -2251,6 +2252,8 @@ DeferredTriggerAbortXact(void)
22512252
void
22522253
DeferredTriggerBeginSubXact(void)
22532254
{
2255+
int my_level = GetCurrentTransactionNestLevel();
2256+
22542257
/*
22552258
* Ignore call if the transaction is in aborted state.
22562259
*/
@@ -2260,7 +2263,7 @@ DeferredTriggerBeginSubXact(void)
22602263
/*
22612264
* Allocate more space in the stacks if needed.
22622265
*/
2263-
if (deferredTriggers->numpushed == deferredTriggers->numalloc)
2266+
while (my_level >= deferredTriggers->numalloc)
22642267
{
22652268
if (deferredTriggers->numalloc == 0)
22662269
{
@@ -2282,32 +2285,28 @@ DeferredTriggerBeginSubXact(void)
22822285
else
22832286
{
22842287
/* repalloc will keep the stacks in the same context */
2285-
deferredTriggers->numalloc *= 2;
2288+
int new_alloc = deferredTriggers->numalloc * 2;
22862289

22872290
deferredTriggers->tail_stack = (DeferredTriggerEvent *)
22882291
repalloc(deferredTriggers->tail_stack,
2289-
deferredTriggers->numalloc * sizeof(DeferredTriggerEvent));
2292+
new_alloc * sizeof(DeferredTriggerEvent));
22902293
deferredTriggers->imm_stack = (DeferredTriggerEvent *)
22912294
repalloc(deferredTriggers->imm_stack,
2292-
deferredTriggers->numalloc * sizeof(DeferredTriggerEvent));
2295+
new_alloc * sizeof(DeferredTriggerEvent));
22932296
deferredTriggers->state_stack = (DeferredTriggerState *)
22942297
repalloc(deferredTriggers->state_stack,
2295-
deferredTriggers->numalloc * sizeof(DeferredTriggerState));
2298+
new_alloc * sizeof(DeferredTriggerState));
2299+
deferredTriggers->numalloc = new_alloc;
22962300
}
22972301
}
22982302

22992303
/*
2300-
* Push the current list position into the stack and reset the
2301-
* pointer.
2304+
* Push the current information into the stack.
23022305
*/
2303-
deferredTriggers->tail_stack[deferredTriggers->numpushed] =
2304-
deferredTriggers->tail_thisxact;
2305-
deferredTriggers->imm_stack[deferredTriggers->numpushed] =
2306-
deferredTriggers->events_imm;
2306+
deferredTriggers->tail_stack[my_level] = deferredTriggers->tail_thisxact;
2307+
deferredTriggers->imm_stack[my_level] = deferredTriggers->events_imm;
23072308
/* State is not saved until/unless changed */
2308-
deferredTriggers->state_stack[deferredTriggers->numpushed] = NULL;
2309-
2310-
deferredTriggers->numpushed++;
2309+
deferredTriggers->state_stack[my_level] = NULL;
23112310
}
23122311

23132312
/*
@@ -2318,6 +2317,7 @@ DeferredTriggerBeginSubXact(void)
23182317
void
23192318
DeferredTriggerEndSubXact(bool isCommit)
23202319
{
2320+
int my_level = GetCurrentTransactionNestLevel();
23212321
DeferredTriggerState state;
23222322

23232323
/*
@@ -2327,28 +2327,28 @@ DeferredTriggerEndSubXact(bool isCommit)
23272327
return;
23282328

23292329
/*
2330-
* Move back the "top of the stack."
2330+
* Pop the prior state if needed.
23312331
*/
2332-
Assert(deferredTriggers->numpushed > 0);
2333-
2334-
deferredTriggers->numpushed--;
2332+
Assert(my_level < deferredTriggers->numalloc);
23352333

23362334
if (isCommit)
23372335
{
23382336
/* If we saved a prior state, we don't need it anymore */
2339-
state = deferredTriggers->state_stack[deferredTriggers->numpushed];
2337+
state = deferredTriggers->state_stack[my_level];
23402338
if (state != NULL)
23412339
pfree(state);
2340+
/* this avoids double pfree if error later: */
2341+
deferredTriggers->state_stack[my_level] = NULL;
23422342
}
23432343
else
23442344
{
23452345
/*
23462346
* Aborting --- restore the pointers from the stacks.
23472347
*/
23482348
deferredTriggers->tail_thisxact =
2349-
deferredTriggers->tail_stack[deferredTriggers->numpushed];
2349+
deferredTriggers->tail_stack[my_level];
23502350
deferredTriggers->events_imm =
2351-
deferredTriggers->imm_stack[deferredTriggers->numpushed];
2351+
deferredTriggers->imm_stack[my_level];
23522352

23532353
/*
23542354
* Cleanup the head and the tail of the list.
@@ -2367,12 +2367,14 @@ DeferredTriggerEndSubXact(bool isCommit)
23672367
* Restore the trigger state. If the saved state is NULL, then
23682368
* this subxact didn't save it, so it doesn't need restoring.
23692369
*/
2370-
state = deferredTriggers->state_stack[deferredTriggers->numpushed];
2370+
state = deferredTriggers->state_stack[my_level];
23712371
if (state != NULL)
23722372
{
23732373
pfree(deferredTriggers->state);
23742374
deferredTriggers->state = state;
23752375
}
2376+
/* this avoids double pfree if error later: */
2377+
deferredTriggers->state_stack[my_level] = NULL;
23762378
}
23772379
}
23782380

@@ -2457,6 +2459,8 @@ DeferredTriggerStateAddItem(DeferredTriggerState state,
24572459
void
24582460
DeferredTriggerSetState(ConstraintsSetStmt *stmt)
24592461
{
2462+
int my_level = GetCurrentTransactionNestLevel();
2463+
24602464
/*
24612465
* Ignore call if we aren't in a transaction.
24622466
*/
@@ -2468,10 +2472,10 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
24682472
* already, save it so it can be restored if the subtransaction
24692473
* aborts.
24702474
*/
2471-
if (deferredTriggers->numpushed > 0 &&
2472-
deferredTriggers->state_stack[deferredTriggers->numpushed - 1] == NULL)
2475+
if (my_level > 1 &&
2476+
deferredTriggers->state_stack[my_level] == NULL)
24732477
{
2474-
deferredTriggers->state_stack[deferredTriggers->numpushed - 1] =
2478+
deferredTriggers->state_stack[my_level] =
24752479
DeferredTriggerStateCopy(deferredTriggers->state);
24762480
}
24772481

src/backend/storage/ipc/sinval.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.72 2004/08/29 05:06:48 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.73 2004/09/06 23:33:35 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1059,8 +1059,14 @@ XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
10591059
break;
10601060
}
10611061
}
1062-
/* We should have found it, unless the cache has overflowed */
1063-
Assert(j >= 0 || MyProc->subxids.overflowed);
1062+
/*
1063+
* Ordinarily we should have found it, unless the cache has overflowed.
1064+
* However it's also possible for this routine to be invoked multiple
1065+
* times for the same subtransaction, in case of an error during
1066+
* AbortSubTransaction. So instead of Assert, emit a debug warning.
1067+
*/
1068+
if (j < 0 && !MyProc->subxids.overflowed)
1069+
elog(WARNING, "did not find subXID %u in MyProc", anxid);
10641070
}
10651071

10661072
for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
@@ -1071,8 +1077,9 @@ XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
10711077
break;
10721078
}
10731079
}
1074-
/* We should have found it, unless the cache has overflowed */
1075-
Assert(j >= 0 || MyProc->subxids.overflowed);
1080+
/* Ordinarily we should have found it, unless the cache has overflowed */
1081+
if (j < 0 && !MyProc->subxids.overflowed)
1082+
elog(WARNING, "did not find subXID %u in MyProc", xid);
10761083

10771084
LWLockRelease(SInvalLock);
10781085
}

0 commit comments

Comments
 (0)