@@ -2102,55 +2102,82 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
2102
2102
2103
2103
/*
2104
2104
* exec_stmt_call
2105
+ *
2106
+ * NOTE: this is used for both CALL and DO statements.
2105
2107
*/
2106
2108
static int
2107
2109
exec_stmt_call (PLpgSQL_execstate * estate , PLpgSQL_stmt_call * stmt )
2108
2110
{
2109
2111
PLpgSQL_expr * expr = stmt -> expr ;
2112
+ SPIPlanPtr orig_plan = expr -> plan ;
2113
+ bool local_plan ;
2114
+ PLpgSQL_variable * volatile cur_target = stmt -> target ;
2110
2115
volatile LocalTransactionId before_lxid ;
2111
2116
LocalTransactionId after_lxid ;
2112
2117
volatile bool pushed_active_snap = false;
2113
2118
volatile int rc ;
2114
2119
2120
+ /*
2121
+ * If not in atomic context, we make a local plan that we'll just use for
2122
+ * this invocation, and will free at the end. Otherwise, transaction ends
2123
+ * would cause errors about plancache leaks.
2124
+ *
2125
+ * XXX This would be fixable with some plancache/resowner surgery
2126
+ * elsewhere, but for now we'll just work around this here.
2127
+ */
2128
+ local_plan = !estate -> atomic ;
2129
+
2115
2130
/* PG_TRY to ensure we clear the plan link, if needed, on failure */
2116
2131
PG_TRY ();
2117
2132
{
2118
2133
SPIPlanPtr plan = expr -> plan ;
2119
2134
ParamListInfo paramLI ;
2120
2135
2121
- if (plan == NULL )
2136
+ /*
2137
+ * Make a plan if we don't have one, or if we need a local one. Note
2138
+ * that we'll overwrite expr->plan either way; the PG_TRY block will
2139
+ * ensure we undo that on the way out, if the plan is local.
2140
+ */
2141
+ if (plan == NULL || local_plan )
2122
2142
{
2143
+ /* Don't let SPI save the plan if it's going to be local */
2144
+ exec_prepare_plan (estate , expr , 0 , !local_plan );
2145
+ plan = expr -> plan ;
2123
2146
2124
2147
/*
2125
- * Don't save the plan if not in atomic context. Otherwise,
2126
- * transaction ends would cause errors about plancache leaks.
2127
- *
2128
- * XXX This would be fixable with some plancache/resowner surgery
2129
- * elsewhere, but for now we'll just work around this here.
2148
+ * A CALL or DO can never be a simple expression. (If it could
2149
+ * be, we'd have to worry about saving/restoring the previous
2150
+ * values of the related expr fields, not just expr->plan.)
2130
2151
*/
2131
- exec_prepare_plan ( estate , expr , 0 , estate -> atomic );
2152
+ Assert (! expr -> expr_simple_expr );
2132
2153
2133
2154
/*
2134
2155
* The procedure call could end transactions, which would upset
2135
2156
* the snapshot management in SPI_execute*, so don't let it do it.
2136
2157
* Instead, we set the snapshots ourselves below.
2137
2158
*/
2138
- plan = expr -> plan ;
2139
2159
plan -> no_snapshots = true;
2140
2160
2141
2161
/*
2142
2162
* Force target to be recalculated whenever the plan changes, in
2143
2163
* case the procedure's argument list has changed.
2144
2164
*/
2145
2165
stmt -> target = NULL ;
2166
+ cur_target = NULL ;
2146
2167
}
2147
2168
2148
2169
/*
2149
2170
* We construct a DTYPE_ROW datum representing the plpgsql variables
2150
2171
* associated with the procedure's output arguments. Then we can use
2151
2172
* exec_move_row() to do the assignments.
2173
+ *
2174
+ * If we're using a local plan, also make a local target; otherwise,
2175
+ * since the above code will force a new plan each time through, we'd
2176
+ * repeatedly leak the memory for the target. (Note: we also leak the
2177
+ * target when a plan change is forced, but that isn't so likely to
2178
+ * cause excessive memory leaks.)
2152
2179
*/
2153
- if (stmt -> is_call && stmt -> target == NULL )
2180
+ if (stmt -> is_call && cur_target == NULL )
2154
2181
{
2155
2182
Node * node ;
2156
2183
FuncExpr * funcexpr ;
@@ -2165,6 +2192,9 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2165
2192
int i ;
2166
2193
ListCell * lc ;
2167
2194
2195
+ /* Use eval_mcontext for any cruft accumulated here */
2196
+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
2197
+
2168
2198
/*
2169
2199
* Get the parsed CallStmt, and look up the called procedure
2170
2200
*/
@@ -2196,17 +2226,20 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2196
2226
ReleaseSysCache (func_tuple );
2197
2227
2198
2228
/*
2199
- * Begin constructing row Datum
2229
+ * Begin constructing row Datum; keep it in fn_cxt if it's to be
2230
+ * long-lived.
2200
2231
*/
2201
- oldcontext = MemoryContextSwitchTo (estate -> func -> fn_cxt );
2232
+ if (!local_plan )
2233
+ MemoryContextSwitchTo (estate -> func -> fn_cxt );
2202
2234
2203
2235
row = (PLpgSQL_row * ) palloc0 (sizeof (PLpgSQL_row ));
2204
2236
row -> dtype = PLPGSQL_DTYPE_ROW ;
2205
2237
row -> refname = "(unnamed row)" ;
2206
2238
row -> lineno = -1 ;
2207
2239
row -> varnos = (int * ) palloc (sizeof (int ) * list_length (funcargs ));
2208
2240
2209
- MemoryContextSwitchTo (oldcontext );
2241
+ if (!local_plan )
2242
+ MemoryContextSwitchTo (get_eval_mcontext (estate ));
2210
2243
2211
2244
/*
2212
2245
* Examine procedure's argument list. Each output arg position
@@ -2250,7 +2283,13 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2250
2283
2251
2284
row -> nfields = nfields ;
2252
2285
2253
- stmt -> target = (PLpgSQL_variable * ) row ;
2286
+ cur_target = (PLpgSQL_variable * ) row ;
2287
+
2288
+ /* We can save and re-use the target datum, if it's not local */
2289
+ if (!local_plan )
2290
+ stmt -> target = cur_target ;
2291
+
2292
+ MemoryContextSwitchTo (oldcontext );
2254
2293
}
2255
2294
2256
2295
paramLI = setup_param_list (estate , expr );
@@ -2273,17 +2312,27 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2273
2312
PG_CATCH ();
2274
2313
{
2275
2314
/*
2276
- * If we aren't saving the plan, unset the pointer. Note that it
2277
- * could have been unset already, in case of a recursive call.
2315
+ * If we are using a local plan, restore the old plan link.
2278
2316
*/
2279
- if (expr -> plan && ! expr -> plan -> saved )
2280
- expr -> plan = NULL ;
2317
+ if (local_plan )
2318
+ expr -> plan = orig_plan ;
2281
2319
PG_RE_THROW ();
2282
2320
}
2283
2321
PG_END_TRY ();
2284
2322
2285
- if (expr -> plan && !expr -> plan -> saved )
2286
- expr -> plan = NULL ;
2323
+ /*
2324
+ * If we are using a local plan, restore the old plan link; then free the
2325
+ * local plan to avoid memory leaks. (Note that the error exit path above
2326
+ * just clears the link without risking calling SPI_freeplan; we expect
2327
+ * that xact cleanup will take care of the mess in that case.)
2328
+ */
2329
+ if (local_plan )
2330
+ {
2331
+ SPIPlanPtr plan = expr -> plan ;
2332
+
2333
+ expr -> plan = orig_plan ;
2334
+ SPI_freeplan (plan );
2335
+ }
2287
2336
2288
2337
if (rc < 0 )
2289
2338
elog (ERROR , "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s" ,
@@ -2319,10 +2368,10 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
2319
2368
{
2320
2369
SPITupleTable * tuptab = SPI_tuptable ;
2321
2370
2322
- if (!stmt -> target )
2371
+ if (!cur_target )
2323
2372
elog (ERROR , "DO statement returned a row" );
2324
2373
2325
- exec_move_row (estate , stmt -> target , tuptab -> vals [0 ], tuptab -> tupdesc );
2374
+ exec_move_row (estate , cur_target , tuptab -> vals [0 ], tuptab -> tupdesc );
2326
2375
}
2327
2376
else if (SPI_processed > 1 )
2328
2377
elog (ERROR , "procedure call returned more than one row" );
0 commit comments