Skip to content

Commit d3e41cb

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 1eef280 commit d3e41cb

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
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.127.4.9 2010/07/05 09:27:49 heikki Exp $
6+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.127.4.10 2010/08/09 18:51:02 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -970,6 +970,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
970970
*/
971971
SPI_restore_connection();
972972

973+
/* Must clean up the econtext too */
974+
exec_eval_cleanup(estate);
975+
973976
/* Look for a matching exception handler */
974977
exceptions = block->exceptions;
975978
for (j = 0; j < exceptions->exceptions_used; j++)
@@ -2067,6 +2070,9 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
20672070
*
20682071
* NB: the result of the evaluation is no longer valid after this is done,
20692072
* unless it is a pass-by-value datatype.
2073+
*
2074+
* NB: if you change this code, see also the hacks in exec_assign_value's
2075+
* PLPGSQL_DTYPE_ARRAYELEM case.
20702076
* ----------
20712077
*/
20722078
static void
@@ -2866,6 +2872,10 @@ exec_assign_expr(PLpgSQL_execstate *estate, PLpgSQL_datum *target,
28662872

28672873
/* ----------
28682874
* exec_assign_value Put a value into a target field
2875+
*
2876+
* Note: in some code paths, this may leak memory in the eval_econtext;
2877+
* we assume that will be cleaned up later by exec_eval_cleanup. We cannot
2878+
* call exec_eval_cleanup here for fear of destroying the input Datum value.
28692879
* ----------
28702880
*/
28712881
static void
@@ -3131,6 +3141,9 @@ exec_assign_value(PLpgSQL_execstate *estate,
31313141

31323142
case PLPGSQL_DTYPE_ARRAYELEM:
31333143
{
3144+
/*
3145+
* Target is an element of an array
3146+
*/
31343147
int nsubscripts;
31353148
int i;
31363149
PLpgSQL_expr *subscripts[MAXDIM];
@@ -3147,10 +3160,19 @@ exec_assign_value(PLpgSQL_execstate *estate,
31473160
coerced_value;
31483161
ArrayType *oldarrayval;
31493162
ArrayType *newarrayval;
3163+
SPITupleTable *save_eval_tuptable;
3164+
3165+
/*
3166+
* We need to do subscript evaluation, which might require
3167+
* evaluating general expressions; and the caller might have
3168+
* done that too in order to prepare the input Datum. We
3169+
* have to save and restore the caller's SPI_execute result,
3170+
* if any.
3171+
*/
3172+
save_eval_tuptable = estate->eval_tuptable;
3173+
estate->eval_tuptable = NULL;
31503174

31513175
/*
3152-
* Target is an element of an array
3153-
*
31543176
* To handle constructs like x[1][2] := something, we have to
31553177
* be prepared to deal with a chain of arrayelem datums.
31563178
* Chase back to find the base array datum, and save the
@@ -3202,8 +3224,23 @@ exec_assign_value(PLpgSQL_execstate *estate,
32023224
subscripts[nsubscripts - 1 - i],
32033225
&subisnull);
32043226
havenullsubscript |= subisnull;
3227+
3228+
/*
3229+
* Clean up in case the subscript expression wasn't simple.
3230+
* We can't do exec_eval_cleanup, but we can do this much
3231+
* (which is safe because the integer subscript value is
3232+
* surely pass-by-value), and we must do it in case the
3233+
* next subscript expression isn't simple either.
3234+
*/
3235+
if (estate->eval_tuptable != NULL)
3236+
SPI_freetuptable(estate->eval_tuptable);
3237+
estate->eval_tuptable = NULL;
32053238
}
32063239

3240+
/* Now we can restore caller's SPI_execute result if any. */
3241+
Assert(estate->eval_tuptable == NULL);
3242+
estate->eval_tuptable = save_eval_tuptable;
3243+
32073244
/*
32083245
* Skip the assignment if we have any nulls in the subscripts
32093246
* or the righthand side. This is pretty bogus but it

src/test/regress/expected/plpgsql.out

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,3 +2103,46 @@ select sp_add_user('user3');
21032103

21042104
drop function sp_add_user(text);
21052105
drop function sp_id_user(text);
2106+
-- Test for appropriate cleanup of non-simple expression evaluations
2107+
-- (bug in all versions prior to August 2010)
2108+
CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
2109+
DECLARE
2110+
arr text[];
2111+
lr text;
2112+
i integer;
2113+
BEGIN
2114+
arr := array[array['foo','bar'], array['baz', 'quux']];
2115+
lr := 'fool';
2116+
i := 1;
2117+
-- use sub-SELECTs to make expressions non-simple
2118+
arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
2119+
RETURN arr;
2120+
END;
2121+
$$ LANGUAGE plpgsql;
2122+
SELECT nonsimple_expr_test();
2123+
nonsimple_expr_test
2124+
-------------------------
2125+
{{foo,fool},{baz,quux}}
2126+
(1 row)
2127+
2128+
DROP FUNCTION nonsimple_expr_test();
2129+
CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
2130+
declare
2131+
i integer NOT NULL := 0;
2132+
begin
2133+
begin
2134+
i := (SELECT NULL::integer); -- should throw error
2135+
exception
2136+
WHEN OTHERS THEN
2137+
i := (SELECT 1::integer);
2138+
end;
2139+
return i;
2140+
end;
2141+
$$ LANGUAGE plpgsql;
2142+
SELECT nonsimple_expr_test();
2143+
nonsimple_expr_test
2144+
---------------------
2145+
1
2146+
(1 row)
2147+
2148+
DROP FUNCTION nonsimple_expr_test();

src/test/regress/sql/plpgsql.sql

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,3 +1807,43 @@ select sp_add_user('user3');
18071807

18081808
drop function sp_add_user(text);
18091809
drop function sp_id_user(text);
1810+
1811+
-- Test for appropriate cleanup of non-simple expression evaluations
1812+
-- (bug in all versions prior to August 2010)
1813+
1814+
CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
1815+
DECLARE
1816+
arr text[];
1817+
lr text;
1818+
i integer;
1819+
BEGIN
1820+
arr := array[array['foo','bar'], array['baz', 'quux']];
1821+
lr := 'fool';
1822+
i := 1;
1823+
-- use sub-SELECTs to make expressions non-simple
1824+
arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
1825+
RETURN arr;
1826+
END;
1827+
$$ LANGUAGE plpgsql;
1828+
1829+
SELECT nonsimple_expr_test();
1830+
1831+
DROP FUNCTION nonsimple_expr_test();
1832+
1833+
CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
1834+
declare
1835+
i integer NOT NULL := 0;
1836+
begin
1837+
begin
1838+
i := (SELECT NULL::integer); -- should throw error
1839+
exception
1840+
WHEN OTHERS THEN
1841+
i := (SELECT 1::integer);
1842+
end;
1843+
return i;
1844+
end;
1845+
$$ LANGUAGE plpgsql;
1846+
1847+
SELECT nonsimple_expr_test();
1848+
1849+
DROP FUNCTION nonsimple_expr_test();

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