summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-10-28 13:01:23 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2010-10-28 13:01:23 -0400
commit2487e8d8c8ca0014d9c02de16d0bdce62da64ad7 (patch)
treee9970a0300d74a2a50da6e3a9bfded4879ae5bf7 /src/test
parent49b5aba40e0b0adb1caaea81c88dd0e54a7d3890 (diff)
Fix plpgsql's handling of "simple" expression evaluation.
In general, expression execution state trees aren't re-entrantly usable, since functions can store private state information in them. For efficiency reasons, plpgsql tries to cache and reuse state trees for "simple" expressions. It can get away with that most of the time, but it can fail if the state tree is dirty from a previous failed execution (as in an example from Alvaro) or is being used recursively (as noted by me). Fix by tracking whether a state tree is in use, and falling back to the "non-simple" code path if so. This results in a pretty considerable speed hit when the non-simple path is taken, but the available alternatives seem even more unpleasant because they add overhead in the simple path. Per idea from Heikki. Back-patch to all supported branches.
Diffstat (limited to 'src/test')
-rw-r--r--src/test/regress/expected/plpgsql.out47
-rw-r--r--src/test/regress/sql/plpgsql.sql42
2 files changed, 89 insertions, 0 deletions
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index f2826f10625..ff1a9604df7 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -2977,3 +2977,50 @@ SELECT nonsimple_expr_test();
(1 row)
DROP FUNCTION nonsimple_expr_test();
+--
+-- Test cases involving recursion and error recovery in simple expressions
+-- (bugs in all versions before October 2010). The problems are most
+-- easily exposed by mutual recursion between plpgsql and sql functions.
+--
+create function recurse(float8) returns float8 as
+$$
+begin
+ if ($1 < 10) then
+ return sql_recurse($1 + 1);
+ else
+ return $1;
+ end if;
+end;
+$$ language plpgsql;
+-- "limit" is to prevent this from being inlined
+create function sql_recurse(float8) returns float8 as
+$$ select recurse($1) limit 1; $$ language sql;
+select recurse(0);
+ recurse
+---------
+ 10
+(1 row)
+
+create function error1(text) returns text language sql as
+$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
+create function error2(p_name_table text) returns text language plpgsql as $$
+begin
+ return error1(p_name_table);
+end$$;
+BEGIN;
+create table public.stuffs (stuff text);
+SAVEPOINT a;
+select error2('nonexistent.stuffs');
+ERROR: schema "nonexistent" does not exist
+CONTEXT: SQL function "error1" statement 1
+PL/pgSQL function "error2" line 2 at return
+ROLLBACK TO a;
+select error2('public.stuffs');
+ error2
+--------
+ stuffs
+(1 row)
+
+rollback;
+drop function error2(p_name_table text);
+drop function error1(text);
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 27ab5afbdff..37bdb72fb7e 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -2480,3 +2480,45 @@ $$ LANGUAGE plpgsql;
SELECT nonsimple_expr_test();
DROP FUNCTION nonsimple_expr_test();
+
+--
+-- Test cases involving recursion and error recovery in simple expressions
+-- (bugs in all versions before October 2010). The problems are most
+-- easily exposed by mutual recursion between plpgsql and sql functions.
+--
+
+create function recurse(float8) returns float8 as
+$$
+begin
+ if ($1 < 10) then
+ return sql_recurse($1 + 1);
+ else
+ return $1;
+ end if;
+end;
+$$ language plpgsql;
+
+-- "limit" is to prevent this from being inlined
+create function sql_recurse(float8) returns float8 as
+$$ select recurse($1) limit 1; $$ language sql;
+
+select recurse(0);
+
+create function error1(text) returns text language sql as
+$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
+
+create function error2(p_name_table text) returns text language plpgsql as $$
+begin
+ return error1(p_name_table);
+end$$;
+
+BEGIN;
+create table public.stuffs (stuff text);
+SAVEPOINT a;
+select error2('nonexistent.stuffs');
+ROLLBACK TO a;
+select error2('public.stuffs');
+rollback;
+
+drop function error2(p_name_table text);
+drop function error1(text);