Skip to content

Commit 46af71f

Browse files
committed
Fix incorrect logic in plpgsql for cleanup after evaluation of non-simple
expressions. We need to deal with this when handling subscripts in an array assignment, and also when catching an exception. In an Assert-enabled build these omissions led to Assert failures, but I think in a normal build the only consequence would be short-term memory leakage; which may explain why this wasn't reported from the field long ago. Back-patch to all supported versions. 7.4 doesn't have exceptions, but otherwise these bugs go all the way back. Heikki Linnakangas and Tom Lane
1 parent 4773198 commit 46af71f

File tree

3 files changed

+123
-3
lines changed

3 files changed

+123
-3
lines changed

src/pl/plpgsql/src/pl_exec.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.262 2010/08/09 02:25:05 tgl Exp $
11+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.263 2010/08/09 18:50:10 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1099,6 +1099,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
10991099
*/
11001100
SPI_restore_connection();
11011101

1102+
/* Must clean up the econtext too */
1103+
exec_eval_cleanup(estate);
1104+
11021105
/* Look for a matching exception handler */
11031106
foreach(e, block->exceptions->exc_list)
11041107
{
@@ -2701,6 +2704,9 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
27012704
*
27022705
* NB: the result of the evaluation is no longer valid after this is done,
27032706
* unless it is a pass-by-value datatype.
2707+
*
2708+
* NB: if you change this code, see also the hacks in exec_assign_value's
2709+
* PLPGSQL_DTYPE_ARRAYELEM case.
27042710
* ----------
27052711
*/
27062712
static void
@@ -3464,6 +3470,10 @@ exec_assign_expr(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
34643470

34653471
/* ----------
34663472
* exec_assign_value Put a value into a target field
3473+
*
3474+
* Note: in some code paths, this may leak memory in the eval_econtext;
3475+
* we assume that will be cleaned up later by exec_eval_cleanup. We cannot
3476+
* call exec_eval_cleanup here for fear of destroying the input Datum value.
34673477
* ----------
34683478
*/
34693479
static void
@@ -3714,6 +3724,9 @@ exec_assign_value(PLpgSQL_execstate *estate,
37143724

37153725
case PLPGSQL_DTYPE_ARRAYELEM:
37163726
{
3727+
/*
3728+
* Target is an element of an array
3729+
*/
37173730
int nsubscripts;
37183731
int i;
37193732
PLpgSQL_expr *subscripts[MAXDIM];
@@ -3729,10 +3742,19 @@ exec_assign_value(PLpgSQL_execstate *estate,
37293742
coerced_value;
37303743
ArrayType *oldarrayval;
37313744
ArrayType *newarrayval;
3745+
SPITupleTable *save_eval_tuptable;
3746+
3747+
/*
3748+
* We need to do subscript evaluation, which might require
3749+
* evaluating general expressions; and the caller might have
3750+
* done that too in order to prepare the input Datum. We
3751+
* have to save and restore the caller's SPI_execute result,
3752+
* if any.
3753+
*/
3754+
save_eval_tuptable = estate->eval_tuptable;
3755+
estate->eval_tuptable = NULL;
37323756

37333757
/*
3734-
* Target is an element of an array
3735-
*
37363758
* To handle constructs like x[1][2] := something, we have to
37373759
* be prepared to deal with a chain of arrayelem datums. Chase
37383760
* back to find the base array datum, and save the subscript
@@ -3786,8 +3808,23 @@ exec_assign_value(PLpgSQL_execstate *estate,
37863808
ereport(ERROR,
37873809
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
37883810
errmsg("array subscript in assignment must not be null")));
3811+
3812+
/*
3813+
* Clean up in case the subscript expression wasn't simple.
3814+
* We can't do exec_eval_cleanup, but we can do this much
3815+
* (which is safe because the integer subscript value is
3816+
* surely pass-by-value), and we must do it in case the
3817+
* next subscript expression isn't simple either.
3818+
*/
3819+
if (estate->eval_tuptable != NULL)
3820+
SPI_freetuptable(estate->eval_tuptable);
3821+
estate->eval_tuptable = NULL;
37893822
}
37903823

3824+
/* Now we can restore caller's SPI_execute result if any. */
3825+
Assert(estate->eval_tuptable == NULL);
3826+
estate->eval_tuptable = save_eval_tuptable;
3827+
37913828
/* Coerce source value to match array element type. */
37923829
coerced_value = exec_simple_cast_value(value,
37933830
valtype,

src/test/regress/expected/plpgsql.out

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3945,6 +3945,49 @@ SELECT * FROM leaker_1(true);
39453945

39463946
DROP FUNCTION leaker_1(bool);
39473947
DROP FUNCTION leaker_2(bool);
3948+
-- Test for appropriate cleanup of non-simple expression evaluations
3949+
-- (bug in all versions prior to August 2010)
3950+
CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
3951+
DECLARE
3952+
arr text[];
3953+
lr text;
3954+
i integer;
3955+
BEGIN
3956+
arr := array[array['foo','bar'], array['baz', 'quux']];
3957+
lr := 'fool';
3958+
i := 1;
3959+
-- use sub-SELECTs to make expressions non-simple
3960+
arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
3961+
RETURN arr;
3962+
END;
3963+
$$ LANGUAGE plpgsql;
3964+
SELECT nonsimple_expr_test();
3965+
nonsimple_expr_test
3966+
-------------------------
3967+
{{foo,fool},{baz,quux}}
3968+
(1 row)
3969+
3970+
DROP FUNCTION nonsimple_expr_test();
3971+
CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
3972+
declare
3973+
i integer NOT NULL := 0;
3974+
begin
3975+
begin
3976+
i := (SELECT NULL::integer); -- should throw error
3977+
exception
3978+
WHEN OTHERS THEN
3979+
i := (SELECT 1::integer);
3980+
end;
3981+
return i;
3982+
end;
3983+
$$ LANGUAGE plpgsql;
3984+
SELECT nonsimple_expr_test();
3985+
nonsimple_expr_test
3986+
---------------------
3987+
1
3988+
(1 row)
3989+
3990+
DROP FUNCTION nonsimple_expr_test();
39483991
-- Test handling of string literals.
39493992
set standard_conforming_strings = off;
39503993
create or replace function strtest() returns text as $$

src/test/regress/sql/plpgsql.sql

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3150,6 +3150,46 @@ SELECT * FROM leaker_1(true);
31503150
DROP FUNCTION leaker_1(bool);
31513151
DROP FUNCTION leaker_2(bool);
31523152

3153+
-- Test for appropriate cleanup of non-simple expression evaluations
3154+
-- (bug in all versions prior to August 2010)
3155+
3156+
CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
3157+
DECLARE
3158+
arr text[];
3159+
lr text;
3160+
i integer;
3161+
BEGIN
3162+
arr := array[array['foo','bar'], array['baz', 'quux']];
3163+
lr := 'fool';
3164+
i := 1;
3165+
-- use sub-SELECTs to make expressions non-simple
3166+
arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
3167+
RETURN arr;
3168+
END;
3169+
$$ LANGUAGE plpgsql;
3170+
3171+
SELECT nonsimple_expr_test();
3172+
3173+
DROP FUNCTION nonsimple_expr_test();
3174+
3175+
CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
3176+
declare
3177+
i integer NOT NULL := 0;
3178+
begin
3179+
begin
3180+
i := (SELECT NULL::integer); -- should throw error
3181+
exception
3182+
WHEN OTHERS THEN
3183+
i := (SELECT 1::integer);
3184+
end;
3185+
return i;
3186+
end;
3187+
$$ LANGUAGE plpgsql;
3188+
3189+
SELECT nonsimple_expr_test();
3190+
3191+
DROP FUNCTION nonsimple_expr_test();
3192+
31533193
-- Test handling of string literals.
31543194

31553195
set standard_conforming_strings = off;

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