Skip to content

Commit 3df9abd

Browse files
author
Neil Conway
committed
ALTER TABLE ADD COLUMN exhibits a significant memory leak when adding a
column with a default expression. In that situation, we need to rewrite the heap relation. To evaluate the new default expression, we use ExecEvalExpr(); however, this can allocate memory in the current memory context, and ATRewriteTable() does not switch out of the active portal's heap memory context. The end result is a rather large memory leak (on the order of gigabytes for a reasonably sized table). This patch changes ATRewriteTable() to switch to the per-tuple memory context before beginning the per-tuple loop. It also removes an explicit heap_freetuple() in the loop, since that is no longer needed. In an unrelated change, I noticed the code was scanning through the attributes of the new tuple descriptor for each tuple of the old table. I changed this to use precomputation, which should slightly speed up the loop. Thanks to steve@deefs.net for reporting the leak.
1 parent d32b3ae commit 3df9abd

File tree

1 file changed

+32
-16
lines changed

1 file changed

+32
-16
lines changed

src/backend/commands/tablecmds.c

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.145 2005/01/27 23:23:55 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.146 2005/02/09 23:17:26 neilc Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -2460,6 +2460,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
24602460
TupleTableSlot *newslot;
24612461
HeapScanDesc scan;
24622462
HeapTuple tuple;
2463+
MemoryContext oldCxt;
2464+
List *dropped_attrs = NIL;
2465+
ListCell *lc;
24632466

24642467
econtext = GetPerTupleExprContext(estate);
24652468

@@ -2480,29 +2483,40 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
24802483
memset(values, 0, i * sizeof(Datum));
24812484
memset(nulls, 'n', i * sizeof(char));
24822485

2486+
/*
2487+
* Any attributes that are dropped according to the new tuple
2488+
* descriptor can be set to NULL. We precompute the list of
2489+
* dropped attributes to avoid needing to do so in the
2490+
* per-tuple loop.
2491+
*/
2492+
for (i = 0; i < newTupDesc->natts; i++)
2493+
{
2494+
if (newTupDesc->attrs[i]->attisdropped)
2495+
dropped_attrs = lappend_int(dropped_attrs, i);
2496+
}
2497+
24832498
/*
24842499
* Scan through the rows, generating a new row if needed and then
24852500
* checking all the constraints.
24862501
*/
24872502
scan = heap_beginscan(oldrel, SnapshotNow, 0, NULL);
24882503

2504+
/*
2505+
* Switch to per-tuple memory context and reset it for each
2506+
* tuple produced, so we don't leak memory.
2507+
*/
2508+
oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
2509+
24892510
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
24902511
{
24912512
if (newrel)
24922513
{
2493-
/*
2494-
* Extract data from old tuple. We can force to null any
2495-
* columns that are deleted according to the new tuple.
2496-
*/
2497-
int natts = newTupDesc->natts;
2498-
2514+
/* Extract data from old tuple */
24992515
heap_deformtuple(tuple, oldTupDesc, values, nulls);
25002516

2501-
for (i = 0; i < natts; i++)
2502-
{
2503-
if (newTupDesc->attrs[i]->attisdropped)
2504-
nulls[i] = 'n';
2505-
}
2517+
/* Set dropped attributes to null in new tuple */
2518+
foreach (lc, dropped_attrs)
2519+
nulls[lfirst_int(lc)] = 'n';
25062520

25072521
/*
25082522
* Process supplied expressions to replace selected
@@ -2526,6 +2540,11 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25262540
nulls[ex->attnum - 1] = ' ';
25272541
}
25282542

2543+
/*
2544+
* Form the new tuple. Note that we don't explicitly
2545+
* pfree it, since the per-tuple memory context will
2546+
* be reset shortly.
2547+
*/
25292548
tuple = heap_formtuple(newTupDesc, values, nulls);
25302549
}
25312550

@@ -2572,17 +2591,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25722591

25732592
/* Write the tuple out to the new relation */
25742593
if (newrel)
2575-
{
25762594
simple_heap_insert(newrel, tuple);
25772595

2578-
heap_freetuple(tuple);
2579-
}
2580-
25812596
ResetExprContext(econtext);
25822597

25832598
CHECK_FOR_INTERRUPTS();
25842599
}
25852600

2601+
MemoryContextSwitchTo(oldCxt);
25862602
heap_endscan(scan);
25872603
}
25882604

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy