LocalTransactionId curlxid = MyProc->lxid;
CachedPlan *cplan;
void *save_setup_arg;
+ bool need_snapshot;
MemoryContext oldcontext;
/*
/*
* We have to do some of the things SPI_execute_plan would do, in
- * particular advance the snapshot if we are in a non-read-only function.
- * Without this, stable functions within the expression would fail to see
- * updates made so far by our own function.
+ * particular push a new snapshot so that stable functions within the
+ * expression can see updates made so far by our own function. However,
+ * we can skip doing that (and just invoke the expression with the same
+ * snapshot passed to our function) in some cases, which is useful because
+ * it's quite expensive relative to the cost of a simple expression. We
+ * can skip it if the expression contains no stable or volatile functions;
+ * immutable functions shouldn't need to see our updates. Also, if this
+ * is a read-only function, we haven't made any updates so again it's okay
+ * to skip.
*/
oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
- if (!estate->readonly_func)
+ need_snapshot = (expr->expr_simple_mutable && !estate->readonly_func);
+ if (need_snapshot)
{
CommandCounterIncrement();
PushActiveSnapshot(GetTransactionSnapshot());
estate->paramLI->parserSetupArg = save_setup_arg;
- if (!estate->readonly_func)
+ if (need_snapshot)
PopActiveSnapshot();
MemoryContextSwitchTo(oldcontext);
/* Also stash away the expression result type */
expr->expr_simple_type = exprType((Node *) tle_expr);
expr->expr_simple_typmod = exprTypmod((Node *) tle_expr);
+ /* We also want to remember if it is immutable or not */
+ expr->expr_simple_mutable = contain_mutable_functions((Node *) tle_expr);
}
/*
int expr_simple_generation; /* plancache generation we checked */
Oid expr_simple_type; /* result type Oid, if simple */
int32 expr_simple_typmod; /* result typmod, if simple */
+ bool expr_simple_mutable; /* true if simple expr is mutable */
/*
* if expr is simple AND prepared in current transaction,