diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 31582c749da..6166797dc0c 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -2043,6 +2043,8 @@ SPI_result_code_string(int code) * SPI_plan_get_plan_sources --- get a SPI plan's underlying list of * CachedPlanSources. * + * CAUTION: there is no check on whether the CachedPlanSources are up-to-date. + * * This is exported so that PL/pgSQL can use it (this beats letting PL/pgSQL * look directly into the SPIPlan for itself). It's not documented in * spi.sgml because we'd just as soon not have too many places using this. diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 4eb05a02b95..8719c731de9 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -2276,8 +2276,8 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt) static PLpgSQL_variable * make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr) { - List *plansources; - CachedPlanSource *plansource; + CachedPlan *cplan; + PlannedStmt *pstmt; CallStmt *stmt; FuncExpr *funcexpr; HeapTuple func_tuple; @@ -2294,16 +2294,15 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr) oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate)); /* - * Get the parsed CallStmt, and look up the called procedure + * Get the parsed CallStmt, and look up the called procedure. We use + * SPI_plan_get_cached_plan to cover the edge case where expr->plan is + * already stale and needs to be updated. */ - plansources = SPI_plan_get_plan_sources(expr->plan); - if (list_length(plansources) != 1) + cplan = SPI_plan_get_cached_plan(expr->plan); + if (cplan == NULL || list_length(cplan->stmt_list) != 1) elog(ERROR, "query for CALL statement is not a CallStmt"); - plansource = (CachedPlanSource *) linitial(plansources); - if (list_length(plansource->query_list) != 1) - elog(ERROR, "query for CALL statement is not a CallStmt"); - stmt = (CallStmt *) linitial_node(Query, - plansource->query_list)->utilityStmt; + pstmt = linitial_node(PlannedStmt, cplan->stmt_list); + stmt = (CallStmt *) pstmt->utilityStmt; if (stmt == NULL || !IsA(stmt, CallStmt)) elog(ERROR, "query for CALL statement is not a CallStmt"); @@ -2383,6 +2382,8 @@ make_callstmt_target(PLpgSQL_execstate *estate, PLpgSQL_expr *expr) row->nfields = nfields; + ReleaseCachedPlan(cplan, CurrentResourceOwner); + MemoryContextSwitchTo(oldcontext); return (PLpgSQL_variable *) row; @@ -4245,8 +4246,9 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, /* * We could look at the raw_parse_tree, but it seems simpler to * check the command tag. Note we should *not* look at the Query - * tree(s), since those are the result of rewriting and could have - * been transmogrified into something else entirely. + * tree(s), since those are the result of rewriting and could be + * stale, or could have been transmogrified into something else + * entirely. */ if (plansource->commandTag == CMDTAG_INSERT || plansource->commandTag == CMDTAG_UPDATE ||