1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-18 02:02:55 +03:00

Fix plpgsql's handling of "simple" expression evaluation.

In general, expression execution state trees aren't re-entrantly usable,
since functions can store private state information in them.
For efficiency reasons, plpgsql tries to cache and reuse state trees for
"simple" expressions.  It can get away with that most of the time, but it
can fail if the state tree is dirty from a previous failed execution (as
in an example from Alvaro) or is being used recursively (as noted by me).

Fix by tracking whether a state tree is in use, and falling back to the
"non-simple" code path if so.  This results in a pretty considerable speed
hit when the non-simple path is taken, but the available alternatives seem
even more unpleasant because they add overhead in the simple path.  Per
idea from Heikki.

Back-patch to all supported branches.
This commit is contained in:
Tom Lane
2010-10-28 13:01:23 -04:00
parent 49b5aba40e
commit 2487e8d8c8
4 changed files with 112 additions and 4 deletions

View File

@@ -3920,7 +3920,14 @@ exec_eval_expr(PLpgSQL_execstate *estate,
* directly
*/
if (expr->expr_simple_expr != NULL)
return exec_eval_simple_expr(estate, expr, isNull, rettype);
{
/*
* If expression is in use in current xact, don't touch it.
*/
if (!(expr->expr_simple_in_use &&
expr->expr_simple_id == estate->eval_estate_simple_id))
return exec_eval_simple_expr(estate, expr, isNull, rettype);
}
rc = exec_run_select(estate, expr, 2, NULL);
if (rc != SPI_OK_SELECT)
@@ -4071,6 +4078,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
{
expr->expr_simple_state = ExecPrepareExpr(expr->expr_simple_expr,
estate->eval_estate);
expr->expr_simple_in_use = false;
expr->expr_simple_id = estate->eval_estate_simple_id;
}
@@ -4130,6 +4138,11 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
}
/*
* Mark expression as busy for the duration of the ExecEvalExpr call.
*/
expr->expr_simple_in_use = true;
/*
* Finally we can call the executor to evaluate the expression
*/
@@ -4137,11 +4150,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
econtext,
isNull,
NULL);
/* Assorted cleanup */
expr->expr_simple_in_use = false;
MemoryContextSwitchTo(oldcontext);
}
PG_CATCH();
{
/* Restore global vars and propagate error */
/* note we intentionally don't reset expr_simple_in_use here */
ActiveSnapshot = saveActiveSnapshot;
PG_RE_THROW();
}
@@ -4741,6 +4758,7 @@ exec_simple_check_plan(PLpgSQL_expr *expr)
*/
expr->expr_simple_expr = tle->expr;
expr->expr_simple_state = NULL;
expr->expr_simple_in_use = false;
expr->expr_simple_id = -1;
/* Also stash away the expression result type */
expr->expr_simple_type = exprType((Node *) tle->expr);

View File

@@ -181,10 +181,11 @@ typedef struct PLpgSQL_expr
/*
* if expr is simple AND prepared in current eval_estate,
* expr_simple_state is valid. Test validity by seeing if expr_simple_id
* matches eval_estate_simple_id.
* expr_simple_state and expr_simple_in_use are valid. Test validity by
* seeing if expr_simple_id matches eval_estate_simple_id.
*/
ExprState *expr_simple_state;
ExprState *expr_simple_state; /* eval tree for expr_simple_expr */
bool expr_simple_in_use; /* true if eval tree is active */
long int expr_simple_id;
/* params to pass to expr */