@@ -2145,55 +2145,82 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
2145
2145
2146
2146
/*
2147
2147
* exec_stmt_call
2148
+ *
2149
+ * NOTE: this is used for both CALL and DO statements.
2148
2150
*/
2149
2151
static int
2150
2152
exec_stmt_call (PLpgSQL_execstate * estate , PLpgSQL_stmt_call * stmt )
2151
2153
{
2152
2154
PLpgSQL_expr * expr = stmt -> expr ;
2155
+ SPIPlanPtr orig_plan = expr -> plan ;
2156
+ bool local_plan ;
2157
+ PLpgSQL_variable * volatile cur_target = stmt -> target ;
2153
2158
volatile LocalTransactionId before_lxid ;
2154
2159
LocalTransactionId after_lxid ;
2155
2160
volatile bool pushed_active_snap = false;
2156
2161
volatile int rc ;
2157
2162
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
+
2158
2173
/* PG_TRY to ensure we clear the plan link, if needed, on failure */
2159
2174
PG_TRY ();
2160
2175
{
2161
2176
SPIPlanPtr plan = expr -> plan ;
2162
2177
ParamListInfo paramLI ;
2163
2178
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 )
2165
2185
{
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 ;
2166
2189
2167
2190
/*
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.)
2173
2194
*/
2174
- exec_prepare_plan ( estate , expr , 0 , estate -> atomic );
2195
+ Assert (! expr -> expr_simple_expr );
2175
2196
2176
2197
/*
2177
2198
* The procedure call could end transactions, which would upset
2178
2199
* the snapshot management in SPI_execute*, so don't let it do it.
2179
2200
* Instead, we set the snapshots ourselves below.
2180
2201
*/
2181
- plan = expr -> plan ;
2182
2202
plan -> no_snapshots = true;
2183
2203
2184
2204
/*
2185
2205
* Force target to be recalculated whenever the plan changes, in
2186
2206
* case the procedure's argument list has changed.
2187
2207
*/
2188
2208
stmt -> target = NULL ;
2209
+ cur_target = NULL ;
2189
2210
}
2190
2211
2191
2212
/*
2192
2213
* We construct a DTYPE_ROW datum representing the plpgsql variables
2193
2214
* associated with the procedure's output arguments. Then we can use
2194
2215
* 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.)
2195
2222
*/
2196
- if (stmt -> is_call && stmt -> target == NULL )
2223
+ if (stmt -> is_call && cur_target == NULL )
2197
2224
{
2198
2225
Node * node ;
2199
2226
FuncExpr * funcexpr ;
@@ -2208,6 +2235,9 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2208
2235
int i ;
2209
2236
ListCell * lc ;
2210
2237
2238
+ /* Use eval_mcontext for any cruft accumulated here */
2239
+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
2240
+
2211
2241
/*
2212
2242
* Get the parsed CallStmt, and look up the called procedure
2213
2243
*/
@@ -2239,17 +2269,20 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2239
2269
ReleaseSysCache (func_tuple );
2240
2270
2241
2271
/*
2242
- * Begin constructing row Datum
2272
+ * Begin constructing row Datum; keep it in fn_cxt if it's to be
2273
+ * long-lived.
2243
2274
*/
2244
- oldcontext = MemoryContextSwitchTo (estate -> func -> fn_cxt );
2275
+ if (!local_plan )
2276
+ MemoryContextSwitchTo (estate -> func -> fn_cxt );
2245
2277
2246
2278
row = (PLpgSQL_row * ) palloc0 (sizeof (PLpgSQL_row ));
2247
2279
row -> dtype = PLPGSQL_DTYPE_ROW ;
2248
2280
row -> refname = "(unnamed row)" ;
2249
2281
row -> lineno = -1 ;
2250
2282
row -> varnos = (int * ) palloc (sizeof (int ) * list_length (funcargs ));
2251
2283
2252
- MemoryContextSwitchTo (oldcontext );
2284
+ if (!local_plan )
2285
+ MemoryContextSwitchTo (get_eval_mcontext (estate ));
2253
2286
2254
2287
/*
2255
2288
* Examine procedure's argument list. Each output arg position
@@ -2293,7 +2326,13 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2293
2326
2294
2327
row -> nfields = nfields ;
2295
2328
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 );
2297
2336
}
2298
2337
2299
2338
paramLI = setup_param_list (estate , expr );
@@ -2316,17 +2355,27 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2316
2355
PG_CATCH ();
2317
2356
{
2318
2357
/*
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.
2321
2359
*/
2322
- if (expr -> plan && ! expr -> plan -> saved )
2323
- expr -> plan = NULL ;
2360
+ if (local_plan )
2361
+ expr -> plan = orig_plan ;
2324
2362
PG_RE_THROW ();
2325
2363
}
2326
2364
PG_END_TRY ();
2327
2365
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
+ }
2330
2379
2331
2380
if (rc < 0 )
2332
2381
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)
2363
2412
{
2364
2413
SPITupleTable * tuptab = SPI_tuptable ;
2365
2414
2366
- if (!stmt -> target )
2415
+ if (!cur_target )
2367
2416
elog (ERROR , "DO statement returned a row" );
2368
2417
2369
- exec_move_row (estate , stmt -> target , tuptab -> vals [0 ], tuptab -> tupdesc );
2418
+ exec_move_row (estate , cur_target , tuptab -> vals [0 ], tuptab -> tupdesc );
2370
2419
}
2371
2420
else if (SPI_processed > 1 )
2372
2421
elog (ERROR , "procedure call returned more than one row" );
0 commit comments