Skip to content

Commit b998655

Browse files
committed
Fix plan cache issue in PL/pgSQL CALL
If we are not going to save the plan, then we need to unset expr->plan after we are done, also in error cases. Otherwise, we get a dangling pointer next time around. This is not the ideal solution. It would be better if we could convince SPI not to associate a cached plan with a resource owner, and then we could just save the plan in all cases. But that would require bigger surgery. Reported-by: Pavel Stehule <pavel.stehule@gmail.com>
1 parent 6a5f796 commit b998655

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

src/pl/plpgsql/src/expected/plpgsql_call.out

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ SELECT * FROM test1;
5353
66
5454
(2 rows)
5555

56+
CALL test_proc4(66);
57+
SELECT * FROM test1;
58+
a
59+
----
60+
66
61+
66
62+
66
63+
66
64+
(4 rows)
65+
5666
-- output arguments
5767
CREATE PROCEDURE test_proc5(INOUT a text)
5868
LANGUAGE plpgsql

src/pl/plpgsql/src/pl_exec.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,7 @@ static int
20602060
exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
20612061
{
20622062
PLpgSQL_expr *expr = stmt->expr;
2063+
SPIPlanPtr plan;
20632064
ParamListInfo paramLI;
20642065
LocalTransactionId before_lxid;
20652066
LocalTransactionId after_lxid;
@@ -2069,7 +2070,9 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
20692070
{
20702071
/*
20712072
* Don't save the plan if not in atomic context. Otherwise,
2072-
* transaction ends would cause warnings about plan leaks.
2073+
* transaction ends would cause errors about plancache leaks. XXX
2074+
* This would be fixable with some plancache/resowner surgery
2075+
* elsewhere, but for now we'll just work around this here.
20732076
*/
20742077
exec_prepare_plan(estate, expr, 0, estate->atomic);
20752078

@@ -2084,8 +2087,27 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
20842087

20852088
before_lxid = MyProc->lxid;
20862089

2087-
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
2088-
estate->readonly_func, 0);
2090+
PG_TRY();
2091+
{
2092+
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
2093+
estate->readonly_func, 0);
2094+
}
2095+
PG_CATCH();
2096+
{
2097+
/*
2098+
* If we aren't saving the plan, unset the pointer. Note that it
2099+
* could have been unset already, in case of a recursive call.
2100+
*/
2101+
if (expr->plan && !expr->plan->saved)
2102+
expr->plan = NULL;
2103+
PG_RE_THROW();
2104+
}
2105+
PG_END_TRY();
2106+
2107+
plan = expr->plan;
2108+
2109+
if (expr->plan && !expr->plan->saved)
2110+
expr->plan = NULL;
20892111

20902112
after_lxid = MyProc->lxid;
20912113

@@ -2129,7 +2151,7 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
21292151
/*
21302152
* Get the original CallStmt
21312153
*/
2132-
node = linitial_node(Query, ((CachedPlanSource *) linitial(expr->plan->plancache_list))->query_list)->utilityStmt;
2154+
node = linitial_node(Query, ((CachedPlanSource *) linitial(plan->plancache_list))->query_list)->utilityStmt;
21332155
if (!IsA(node, CallStmt))
21342156
elog(ERROR, "returned row from not a CallStmt");
21352157

src/pl/plpgsql/src/sql/plpgsql_call.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ CALL test_proc4(66);
5454

5555
SELECT * FROM test1;
5656

57+
CALL test_proc4(66);
58+
59+
SELECT * FROM test1;
60+
5761

5862
-- output arguments
5963

0 commit comments

Comments
 (0)