Commit 03cd7571 authored by Tom Lane's avatar Tom Lane

Fix the plpgsql memory leak exhibited in bug #4677. That leak was introduced

by my patch of 2007-01-28 to use per-subtransaction ExprContexts/EStates:
since we re-prepared any expression tree when the current subtransaction ID
changed, we'd accumulate more and more leaked expression state trees in the
outermost subtransaction if the same function was executed at multiple levels
of subtransaction nesting.  To fix, go back to the previous scheme where
there was only one EState per transaction for simple plpgsql expressions.
We really only need an ExprContext per subtransaction, not a whole EState,
so it's possible to keep prepared expression state trees in the one EState
throughout the transaction.  This should be more efficient as well as not
leaking memory for cases involving lots of subtransactions.

The added regression test is the case that inspired the 2007-01-28 patch in
the first place, just to make sure we didn't go backwards.  The current
memory leak complaint is unfortunately hard to test for in the regression
test framework, though manual testing shows it's fixed.

Although this is a pre-existing bug, I'm not back-patching because I'd like to
see this method get some field testing first.  Consider back-patching if it
gets through 8.4beta unscathed.
parent 4703250a
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.109 2009/02/17 11:34:34 petere Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.110 2009/04/09 02:57:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -205,12 +205,12 @@ typedef struct PLpgSQL_expr
Oid expr_simple_type; /* result type Oid, if simple */
/*
* if expr is simple AND prepared in current eval_estate,
* expr_simple_state is valid. Test validity by seeing if expr_simple_id
* matches eval_estate_simple_id.
* if expr is simple AND prepared in current transaction,
* expr_simple_state is valid. Test validity by seeing if expr_simple_lxid
* matches current LXID.
*/
ExprState *expr_simple_state;
long int expr_simple_id;
LocalTransactionId expr_simple_lxid;
/* params to pass to expr */
int nparams;
......@@ -719,8 +719,6 @@ typedef struct
uint32 eval_processed;
Oid eval_lastoid;
ExprContext *eval_econtext; /* for executing simple expressions */
EState *eval_estate; /* EState containing eval_econtext */
long int eval_estate_simple_id; /* ID for eval_estate */
/* status information for error context reporting */
PLpgSQL_function *err_func; /* current func */
......
......@@ -3698,3 +3698,42 @@ NOTICE: f 0
(4 rows)
drop function rttest();
-- Test for proper cleanup at subtransaction exit. This example
-- exposed a bug in PG 8.2.
CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
DECLARE
v_var INTEGER;
BEGIN
BEGIN
v_var := (leaker_2(fail)).error_code;
EXCEPTION
WHEN others THEN RETURN 0;
END;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
RETURNS RECORD AS $$
BEGIN
IF fail THEN
RAISE EXCEPTION 'fail ...';
END IF;
error_code := 1;
new_id := 1;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM leaker_1(false);
leaker_1
----------
1
(1 row)
SELECT * FROM leaker_1(true);
leaker_1
----------
0
(1 row)
DROP FUNCTION leaker_1(bool);
DROP FUNCTION leaker_2(bool);
......@@ -2972,3 +2972,36 @@ select * from rttest();
drop function rttest();
-- Test for proper cleanup at subtransaction exit. This example
-- exposed a bug in PG 8.2.
CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
DECLARE
v_var INTEGER;
BEGIN
BEGIN
v_var := (leaker_2(fail)).error_code;
EXCEPTION
WHEN others THEN RETURN 0;
END;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
RETURNS RECORD AS $$
BEGIN
IF fail THEN
RAISE EXCEPTION 'fail ...';
END IF;
error_code := 1;
new_id := 1;
RETURN;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM leaker_1(false);
SELECT * FROM leaker_1(true);
DROP FUNCTION leaker_1(bool);
DROP FUNCTION leaker_2(bool);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment