int64 availMemLessRefund;
int memtupsize = state->memtupsize;
+ /* Caller error if we have no tapes */
+ Assert(state->activeTapes > 0);
+
/* For simplicity, assume no memtuples are actually currently counted */
Assert(state->memtupcount == 0);
refund = memtupsize * STANDARDCHUNKHEADERSIZE;
availMemLessRefund = state->availMem - refund;
+ /*
+ * We need to be sure that we do not cause LACKMEM to become true, else
+ * the batch allocation size could be calculated as negative, causing
+ * havoc. Hence, if availMemLessRefund is negative at this point, we must
+ * do nothing. Moreover, if it's positive but rather small, there's
+ * little point in proceeding because we could only increase memtuples by
+ * a small amount, not worth the cost of the repalloc's. We somewhat
+ * arbitrarily set the threshold at ALLOCSET_DEFAULT_INITSIZE per tape.
+ * (Note that this does not represent any assumption about tuple sizes.)
+ */
+ if (availMemLessRefund <=
+ (int64) state->activeTapes * ALLOCSET_DEFAULT_INITSIZE)
+ return;
+
/*
* To establish balanced memory use after refunding palloc overhead,
* temporarily have our accounting indicate that we've allocated all
state->growmemtuples = true;
USEMEM(state, availMemLessRefund);
(void) grow_memtuples(state);
- /* Should not matter, but be tidy */
- FREEMEM(state, availMemLessRefund);
state->growmemtuples = false;
+ /* availMem must stay accurate for spacePerTape calculation */
+ FREEMEM(state, availMemLessRefund);
+ if (LACKMEM(state))
+ elog(ERROR, "unexpected out-of-memory situation in tuplesort");
#ifdef TRACE_SORT
if (trace_sort)