@@ -2145,55 +2145,82 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
21452145
21462146/*
21472147 * exec_stmt_call
2148+ *
2149+ * NOTE: this is used for both CALL and DO statements.
21482150 */
21492151static int
21502152exec_stmt_call (PLpgSQL_execstate * estate , PLpgSQL_stmt_call * stmt )
21512153{
21522154 PLpgSQL_expr * expr = stmt -> expr ;
2155+ SPIPlanPtr orig_plan = expr -> plan ;
2156+ bool local_plan ;
2157+ PLpgSQL_variable * volatile cur_target = stmt -> target ;
21532158 volatile LocalTransactionId before_lxid ;
21542159 LocalTransactionId after_lxid ;
21552160 volatile bool pushed_active_snap = false;
21562161 volatile int rc ;
21572162
2163+ /*
2164+ * If not in atomic context, we make a local plan that we'll just use for
2165+ * this invocation, and will free at the end. Otherwise, transaction ends
2166+ * would cause errors about plancache leaks.
2167+ *
2168+ * XXX This would be fixable with some plancache/resowner surgery
2169+ * elsewhere, but for now we'll just work around this here.
2170+ */
2171+ local_plan = !estate -> atomic ;
2172+
21582173 /* PG_TRY to ensure we clear the plan link, if needed, on failure */
21592174 PG_TRY ();
21602175 {
21612176 SPIPlanPtr plan = expr -> plan ;
21622177 ParamListInfo paramLI ;
21632178
2164- if (plan == NULL )
2179+ /*
2180+ * Make a plan if we don't have one, or if we need a local one. Note
2181+ * that we'll overwrite expr->plan either way; the PG_TRY block will
2182+ * ensure we undo that on the way out, if the plan is local.
2183+ */
2184+ if (plan == NULL || local_plan )
21652185 {
2186+ /* Don't let SPI save the plan if it's going to be local */
2187+ exec_prepare_plan (estate , expr , 0 , !local_plan );
2188+ plan = expr -> plan ;
21662189
21672190 /*
2168- * Don't save the plan if not in atomic context. Otherwise,
2169- * transaction ends would cause errors about plancache leaks.
2170- *
2171- * XXX This would be fixable with some plancache/resowner surgery
2172- * elsewhere, but for now we'll just work around this here.
2191+ * A CALL or DO can never be a simple expression. (If it could
2192+ * be, we'd have to worry about saving/restoring the previous
2193+ * values of the related expr fields, not just expr->plan.)
21732194 */
2174- exec_prepare_plan ( estate , expr , 0 , estate -> atomic );
2195+ Assert (! expr -> expr_simple_expr );
21752196
21762197 /*
21772198 * The procedure call could end transactions, which would upset
21782199 * the snapshot management in SPI_execute*, so don't let it do it.
21792200 * Instead, we set the snapshots ourselves below.
21802201 */
2181- plan = expr -> plan ;
21822202 plan -> no_snapshots = true;
21832203
21842204 /*
21852205 * Force target to be recalculated whenever the plan changes, in
21862206 * case the procedure's argument list has changed.
21872207 */
21882208 stmt -> target = NULL ;
2209+ cur_target = NULL ;
21892210 }
21902211
21912212 /*
21922213 * We construct a DTYPE_ROW datum representing the plpgsql variables
21932214 * associated with the procedure's output arguments. Then we can use
21942215 * exec_move_row() to do the assignments.
2216+ *
2217+ * If we're using a local plan, also make a local target; otherwise,
2218+ * since the above code will force a new plan each time through, we'd
2219+ * repeatedly leak the memory for the target. (Note: we also leak the
2220+ * target when a plan change is forced, but that isn't so likely to
2221+ * cause excessive memory leaks.)
21952222 */
2196- if (stmt -> is_call && stmt -> target == NULL )
2223+ if (stmt -> is_call && cur_target == NULL )
21972224 {
21982225 Node * node ;
21992226 FuncExpr * funcexpr ;
@@ -2208,6 +2235,9 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
22082235 int i ;
22092236 ListCell * lc ;
22102237
2238+ /* Use eval_mcontext for any cruft accumulated here */
2239+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
2240+
22112241 /*
22122242 * Get the parsed CallStmt, and look up the called procedure
22132243 */
@@ -2239,17 +2269,20 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
22392269 ReleaseSysCache (func_tuple );
22402270
22412271 /*
2242- * Begin constructing row Datum
2272+ * Begin constructing row Datum; keep it in fn_cxt if it's to be
2273+ * long-lived.
22432274 */
2244- oldcontext = MemoryContextSwitchTo (estate -> func -> fn_cxt );
2275+ if (!local_plan )
2276+ MemoryContextSwitchTo (estate -> func -> fn_cxt );
22452277
22462278 row = (PLpgSQL_row * ) palloc0 (sizeof (PLpgSQL_row ));
22472279 row -> dtype = PLPGSQL_DTYPE_ROW ;
22482280 row -> refname = "(unnamed row)" ;
22492281 row -> lineno = -1 ;
22502282 row -> varnos = (int * ) palloc (sizeof (int ) * list_length (funcargs ));
22512283
2252- MemoryContextSwitchTo (oldcontext );
2284+ if (!local_plan )
2285+ MemoryContextSwitchTo (get_eval_mcontext (estate ));
22532286
22542287 /*
22552288 * Examine procedure's argument list. Each output arg position
@@ -2293,7 +2326,13 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
22932326
22942327 row -> nfields = nfields ;
22952328
2296- stmt -> target = (PLpgSQL_variable * ) row ;
2329+ cur_target = (PLpgSQL_variable * ) row ;
2330+
2331+ /* We can save and re-use the target datum, if it's not local */
2332+ if (!local_plan )
2333+ stmt -> target = cur_target ;
2334+
2335+ MemoryContextSwitchTo (oldcontext );
22972336 }
22982337
22992338 paramLI = setup_param_list (estate , expr );
@@ -2316,17 +2355,27 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
23162355 PG_CATCH ();
23172356 {
23182357 /*
2319- * If we aren't saving the plan, unset the pointer. Note that it
2320- * could have been unset already, in case of a recursive call.
2358+ * If we are using a local plan, restore the old plan link.
23212359 */
2322- if (expr -> plan && ! expr -> plan -> saved )
2323- expr -> plan = NULL ;
2360+ if (local_plan )
2361+ expr -> plan = orig_plan ;
23242362 PG_RE_THROW ();
23252363 }
23262364 PG_END_TRY ();
23272365
2328- if (expr -> plan && !expr -> plan -> saved )
2329- expr -> plan = NULL ;
2366+ /*
2367+ * If we are using a local plan, restore the old plan link; then free the
2368+ * local plan to avoid memory leaks. (Note that the error exit path above
2369+ * just clears the link without risking calling SPI_freeplan; we expect
2370+ * that xact cleanup will take care of the mess in that case.)
2371+ */
2372+ if (local_plan )
2373+ {
2374+ SPIPlanPtr plan = expr -> plan ;
2375+
2376+ expr -> plan = orig_plan ;
2377+ SPI_freeplan (plan );
2378+ }
23302379
23312380 if (rc < 0 )
23322381 elog (ERROR , "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s" ,
@@ -2363,10 +2412,10 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
23632412 {
23642413 SPITupleTable * tuptab = SPI_tuptable ;
23652414
2366- if (!stmt -> target )
2415+ if (!cur_target )
23672416 elog (ERROR , "DO statement returned a row" );
23682417
2369- exec_move_row (estate , stmt -> target , tuptab -> vals [0 ], tuptab -> tupdesc );
2418+ exec_move_row (estate , cur_target , tuptab -> vals [0 ], tuptab -> tupdesc );
23702419 }
23712420 else if (SPI_processed > 1 )
23722421 elog (ERROR , "procedure call returned more than one row" );
0 commit comments