@@ -2134,55 +2134,82 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
2134
2134
2135
2135
/*
2136
2136
* exec_stmt_call
2137
+ *
2138
+ * NOTE: this is used for both CALL and DO statements.
2137
2139
*/
2138
2140
static int
2139
2141
exec_stmt_call (PLpgSQL_execstate * estate , PLpgSQL_stmt_call * stmt )
2140
2142
{
2141
2143
PLpgSQL_expr * expr = stmt -> expr ;
2144
+ SPIPlanPtr orig_plan = expr -> plan ;
2145
+ bool local_plan ;
2146
+ PLpgSQL_variable * volatile cur_target = stmt -> target ;
2142
2147
volatile LocalTransactionId before_lxid ;
2143
2148
LocalTransactionId after_lxid ;
2144
2149
volatile bool pushed_active_snap = false;
2145
2150
volatile int rc ;
2146
2151
2152
+ /*
2153
+ * If not in atomic context, we make a local plan that we'll just use for
2154
+ * this invocation, and will free at the end. Otherwise, transaction ends
2155
+ * would cause errors about plancache leaks.
2156
+ *
2157
+ * XXX This would be fixable with some plancache/resowner surgery
2158
+ * elsewhere, but for now we'll just work around this here.
2159
+ */
2160
+ local_plan = !estate -> atomic ;
2161
+
2147
2162
/* PG_TRY to ensure we clear the plan link, if needed, on failure */
2148
2163
PG_TRY ();
2149
2164
{
2150
2165
SPIPlanPtr plan = expr -> plan ;
2151
2166
ParamListInfo paramLI ;
2152
2167
2153
- if (plan == NULL )
2168
+ /*
2169
+ * Make a plan if we don't have one, or if we need a local one. Note
2170
+ * that we'll overwrite expr->plan either way; the PG_TRY block will
2171
+ * ensure we undo that on the way out, if the plan is local.
2172
+ */
2173
+ if (plan == NULL || local_plan )
2154
2174
{
2175
+ /* Don't let SPI save the plan if it's going to be local */
2176
+ exec_prepare_plan (estate , expr , 0 , !local_plan );
2177
+ plan = expr -> plan ;
2155
2178
2156
2179
/*
2157
- * Don't save the plan if not in atomic context. Otherwise,
2158
- * transaction ends would cause errors about plancache leaks.
2159
- *
2160
- * XXX This would be fixable with some plancache/resowner surgery
2161
- * elsewhere, but for now we'll just work around this here.
2180
+ * A CALL or DO can never be a simple expression. (If it could
2181
+ * be, we'd have to worry about saving/restoring the previous
2182
+ * values of the related expr fields, not just expr->plan.)
2162
2183
*/
2163
- exec_prepare_plan ( estate , expr , 0 , estate -> atomic );
2184
+ Assert (! expr -> expr_simple_expr );
2164
2185
2165
2186
/*
2166
2187
* The procedure call could end transactions, which would upset
2167
2188
* the snapshot management in SPI_execute*, so don't let it do it.
2168
2189
* Instead, we set the snapshots ourselves below.
2169
2190
*/
2170
- plan = expr -> plan ;
2171
2191
plan -> no_snapshots = true;
2172
2192
2173
2193
/*
2174
2194
* Force target to be recalculated whenever the plan changes, in
2175
2195
* case the procedure's argument list has changed.
2176
2196
*/
2177
2197
stmt -> target = NULL ;
2198
+ cur_target = NULL ;
2178
2199
}
2179
2200
2180
2201
/*
2181
2202
* We construct a DTYPE_ROW datum representing the plpgsql variables
2182
2203
* associated with the procedure's output arguments. Then we can use
2183
2204
* exec_move_row() to do the assignments.
2205
+ *
2206
+ * If we're using a local plan, also make a local target; otherwise,
2207
+ * since the above code will force a new plan each time through, we'd
2208
+ * repeatedly leak the memory for the target. (Note: we also leak the
2209
+ * target when a plan change is forced, but that isn't so likely to
2210
+ * cause excessive memory leaks.)
2184
2211
*/
2185
- if (stmt -> is_call && stmt -> target == NULL )
2212
+ if (stmt -> is_call && cur_target == NULL )
2186
2213
{
2187
2214
Node * node ;
2188
2215
FuncExpr * funcexpr ;
@@ -2197,6 +2224,9 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2197
2224
int i ;
2198
2225
ListCell * lc ;
2199
2226
2227
+ /* Use eval_mcontext for any cruft accumulated here */
2228
+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
2229
+
2200
2230
/*
2201
2231
* Get the parsed CallStmt, and look up the called procedure
2202
2232
*/
@@ -2228,17 +2258,20 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2228
2258
ReleaseSysCache (func_tuple );
2229
2259
2230
2260
/*
2231
- * Begin constructing row Datum
2261
+ * Begin constructing row Datum; keep it in fn_cxt if it's to be
2262
+ * long-lived.
2232
2263
*/
2233
- oldcontext = MemoryContextSwitchTo (estate -> func -> fn_cxt );
2264
+ if (!local_plan )
2265
+ MemoryContextSwitchTo (estate -> func -> fn_cxt );
2234
2266
2235
2267
row = (PLpgSQL_row * ) palloc0 (sizeof (PLpgSQL_row ));
2236
2268
row -> dtype = PLPGSQL_DTYPE_ROW ;
2237
2269
row -> refname = "(unnamed row)" ;
2238
2270
row -> lineno = -1 ;
2239
2271
row -> varnos = (int * ) palloc (sizeof (int ) * list_length (funcargs ));
2240
2272
2241
- MemoryContextSwitchTo (oldcontext );
2273
+ if (!local_plan )
2274
+ MemoryContextSwitchTo (get_eval_mcontext (estate ));
2242
2275
2243
2276
/*
2244
2277
* Examine procedure's argument list. Each output arg position
@@ -2282,7 +2315,13 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2282
2315
2283
2316
row -> nfields = nfields ;
2284
2317
2285
- stmt -> target = (PLpgSQL_variable * ) row ;
2318
+ cur_target = (PLpgSQL_variable * ) row ;
2319
+
2320
+ /* We can save and re-use the target datum, if it's not local */
2321
+ if (!local_plan )
2322
+ stmt -> target = cur_target ;
2323
+
2324
+ MemoryContextSwitchTo (oldcontext );
2286
2325
}
2287
2326
2288
2327
paramLI = setup_param_list (estate , expr );
@@ -2305,17 +2344,27 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2305
2344
PG_CATCH ();
2306
2345
{
2307
2346
/*
2308
- * If we aren't saving the plan, unset the pointer. Note that it
2309
- * could have been unset already, in case of a recursive call.
2347
+ * If we are using a local plan, restore the old plan link.
2310
2348
*/
2311
- if (expr -> plan && ! expr -> plan -> saved )
2312
- expr -> plan = NULL ;
2349
+ if (local_plan )
2350
+ expr -> plan = orig_plan ;
2313
2351
PG_RE_THROW ();
2314
2352
}
2315
2353
PG_END_TRY ();
2316
2354
2317
- if (expr -> plan && !expr -> plan -> saved )
2318
- expr -> plan = NULL ;
2355
+ /*
2356
+ * If we are using a local plan, restore the old plan link; then free the
2357
+ * local plan to avoid memory leaks. (Note that the error exit path above
2358
+ * just clears the link without risking calling SPI_freeplan; we expect
2359
+ * that xact cleanup will take care of the mess in that case.)
2360
+ */
2361
+ if (local_plan )
2362
+ {
2363
+ SPIPlanPtr plan = expr -> plan ;
2364
+
2365
+ expr -> plan = orig_plan ;
2366
+ SPI_freeplan (plan );
2367
+ }
2319
2368
2320
2369
if (rc < 0 )
2321
2370
elog (ERROR , "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s" ,
@@ -2352,10 +2401,10 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2352
2401
{
2353
2402
SPITupleTable * tuptab = SPI_tuptable ;
2354
2403
2355
- if (!stmt -> target )
2404
+ if (!cur_target )
2356
2405
elog (ERROR , "DO statement returned a row" );
2357
2406
2358
- exec_move_row (estate , stmt -> target , tuptab -> vals [0 ], tuptab -> tupdesc );
2407
+ exec_move_row (estate , cur_target , tuptab -> vals [0 ], tuptab -> tupdesc );
2359
2408
}
2360
2409
else if (SPI_processed > 1 )
2361
2410
elog (ERROR , "procedure call returned more than one row" );
0 commit comments