postgres.git / summary / log / commit / refs

commit    1d5edff1b0a67fe75946ba8ff58d93b13de77e54
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Tue Nov 09 16:28:18 2010 +0000
First released in: 9.1.0, 8.1.23, 8.2.19, 8.3.13, 8.4.6, 9.0.2

    Repair memory leakage while ANALYZE-ing complex index expressions.
    
    The general design of memory management in Postgres is that intermediate
    results computed by an expression are not freed until the end of the tuple
    cycle.  For expression indexes, ANALYZE has to re-evaluate each expression
    for each of its sample rows, and it wasn't bothering to free intermediate
    results until the end of processing of that index.  This could lead to very
    substantial leakage if the intermediate results were large, as in a recent
    example from Jakub Ouhrabka.  Fix by doing ResetExprContext for each sample
    row.  This necessitates adding a datumCopy step to ensure that the final
    expression value isn't recycled too.  Some quick testing suggests that this
    change adds at worst about 10% to the time needed to analyze a table with
    an expression index; which is annoying, but seems a tolerable price to pay
    to avoid unexpected out-of-memory problems.
    
    Back-patch to all supported branches.


src/backend/commands/analyze.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 9291b03c29f..2f78977ea8c 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -529,6 +529,12 @@ compute_index_stats(Relation onerel, double totalrows, { HeapTuple heapTuple = rows[rowno]; + /* + * Reset the per-tuple context each time, to reclaim any cruft + * left behind by evaluating the predicate or index expressions. + */ + ResetExprContext(econtext); + /* Set up for predicate or expression evaluation */ ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); @@ -553,15 +559,26 @@ compute_index_stats(Relation onerel, double totalrows, isnull); /* - * Save just the columns we care about. + * Save just the columns we care about. We copy the values + * into ind_context from the estate's per-tuple context. */ for (i = 0; i < attr_cnt; i++) { VacAttrStats *stats = thisdata->vacattrstats[i]; int attnum = stats->attr->attnum; - exprvals[tcnt] = values[attnum - 1]; - exprnulls[tcnt] = isnull[attnum - 1]; + if (isnull[attnum - 1]) + { + exprvals[tcnt] = (Datum) 0; + exprnulls[tcnt] = true; + } + else + { + exprvals[tcnt] = datumCopy(values[attnum - 1], + stats->attrtype->typbyval, + stats->attrtype->typlen); + exprnulls[tcnt] = false; + } tcnt++; } } [parent: 17b38fae86f0]