diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd799b724d..fbe4708736 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -21,6 +21,7 @@
 #include "access/htup_details.h"
 #include "access/transam.h"
 #include "access/tupconvert.h"
+#include "catalog/catalog.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -6155,7 +6156,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 	 * updates made so far by our own function.
 	 */
 	oldcontext = MemoryContextSwitchTo(get_eval_mcontext(estate));
-	if (!estate->readonly_func)
+	if (!estate->readonly_func && expr->expr_needs_snapshot)
 	{
 		CommandCounterIncrement();
 		PushActiveSnapshot(GetTransactionSnapshot());
@@ -6180,7 +6181,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
 
 	estate->paramLI->parserSetupArg = save_setup_arg;
 
-	if (!estate->readonly_func)
+	if (!estate->readonly_func && expr->expr_needs_snapshot)
 		PopActiveSnapshot();
 
 	MemoryContextSwitchTo(oldcontext);
@@ -7975,6 +7976,39 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
 	ReleaseCachedPlan(cplan, true);
 }
 
+/*
+ * expr_needs_snapshot --- check if expression contains calls of non-immutable
+ *						   functions or subqueries
+ */
+static bool
+expr_needs_snapshot(Node *expr, void *ctx)
+{
+	if (expr == NULL)
+		return false;
+
+	if (IsA(expr, FuncExpr))
+	{
+		FuncExpr *func = (FuncExpr *) expr;
+
+		if ((func_volatile(func->funcid) != PROVOLATILE_IMMUTABLE) ||
+			!IsCatalogNamespace(get_func_namespace(func->funcid)))
+			return true;
+	}
+	else if (IsA(expr, OpExpr))
+	{
+		OpExpr *op = (OpExpr *) expr;
+
+		if ((func_volatile(op->opfuncid) != PROVOLATILE_IMMUTABLE) ||
+			!IsCatalogNamespace(get_func_namespace(op->opfuncid)))
+			return true;
+	}
+	else if (IsA(expr, Query) || IsA(expr, SubPlan) ||
+			 IsA(expr, AlternativeSubPlan) || IsA(expr, SubLink))
+		return true;
+
+	return expression_tree_walker(expr, expr_needs_snapshot, ctx);
+}
+
 /*
  * exec_save_simple_expr --- extract simple expression from CachedPlan
  */
@@ -8044,6 +8078,7 @@ exec_save_simple_expr(PLpgSQL_expr *expr, CachedPlan *cplan)
 	 * current transaction".
 	 */
 	expr->expr_simple_expr = tle_expr;
+	expr->expr_needs_snapshot = expr_needs_snapshot((Node *) tle_expr, NULL);
 	expr->expr_simple_generation = cplan->generation;
 	expr->expr_simple_state = NULL;
 	expr->expr_simple_in_use = false;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index f66b2baa70..b745f5b3a5 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -243,6 +243,13 @@ typedef struct PLpgSQL_expr
 	 */
 	ExprState  *expr_simple_state;	/* eval tree for expr_simple_expr */
 	bool		expr_simple_in_use; /* true if eval tree is active */
+
+	/*
+	 * when expression use only buildin immutable functions, then it
+	 * doesn't need special snapshot. For other cases this value is true
+	 * for volatile functions and false for any other.
+	 */
+	bool		expr_needs_snapshot;
 	LocalTransactionId expr_simple_lxid;
 } PLpgSQL_expr;
 
