This is an automated email from the ASF dual-hosted git repository.

starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 820f51b474a [refactor](fe) Refine volatile expression handling (#63403)
820f51b474a is described below

commit 820f51b474a7c28fbfd1b705c922226cefe8d971
Author: yujun <[email protected]>
AuthorDate: Wed May 20 14:38:29 2026 +0800

    [refactor](fe) Refine volatile expression handling (#63403)
    
    Related PR: #62698
    
    Problem Summary:
    
    PR #62698 introduced the UDF volatility property and added
    `VolatileExpression` / `VolatileIdentity` so volatile UDF calls can
    carry per-call identity and avoid unsafe optimizer rewrites.
    
    This PR is a follow-up refactoring for that change. It removes
    duplicated unique identity state from `UniqueFunction`, keeps
    `VolatileIdentity` as the single identity holder, moves
    volatile-expression helper methods into `ExpressionTrait`, and expands
    the former `AddProjectForUniqueFunction` rewrite to operate on volatile
    expressions rather than only unique functions.
---
 .../doris/nereids/jobs/executor/Rewriter.java      |  8 ++--
 .../org/apache/doris/nereids/rules/RuleType.java   |  2 +-
 .../nereids/rules/analysis/BindExpression.java     | 13 ++++--
 ...n.java => AddProjectForVolatileExpression.java} | 54 +++++++++++-----------
 .../rewrite/MergeOneRowRelationIntoUnion.java      |  6 +--
 .../PushDownVirtualColumnsIntoOlapScan.java        |  2 +-
 .../nereids/trees/expressions/Expression.java      |  5 --
 .../trees/expressions/VolatileExpression.java      |  5 +-
 .../expressions/functions/ExpressionTrait.java     | 13 ++++++
 .../trees/expressions/functions/scalar/Random.java | 27 +++--------
 .../expressions/functions/scalar/RandomBytes.java  | 11 ++---
 .../functions/scalar/UniqueFunction.java           | 39 ++++++++--------
 .../functions/scalar/UniqueFunctionParams.java     | 17 ++-----
 .../trees/expressions/functions/scalar/Uuid.java   | 11 ++---
 .../expressions/functions/scalar/UuidNumeric.java  | 11 ++---
 .../apache/doris/nereids/util/ExpressionUtils.java | 14 +++---
 ...va => AddProjectForVolatileExpressionTest.java} | 43 ++++++++++++++---
 .../functions/udf/UdfVolatilityTest.java           |  4 +-
 18 files changed, 151 insertions(+), 134 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index 77a23d3633b..e118b21b7e9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -36,7 +36,7 @@ import 
org.apache.doris.nereids.rules.expression.NullableDependentExpressionRewr
 import org.apache.doris.nereids.rules.expression.QueryColumnCollector;
 import org.apache.doris.nereids.rules.rewrite.AddDefaultLimit;
 import org.apache.doris.nereids.rules.rewrite.AddProjectForJoin;
-import org.apache.doris.nereids.rules.rewrite.AddProjectForUniqueFunction;
+import org.apache.doris.nereids.rules.rewrite.AddProjectForVolatileExpression;
 import org.apache.doris.nereids.rules.rewrite.AdjustConjunctsReturnType;
 import org.apache.doris.nereids.rules.rewrite.AdjustNullable;
 import 
org.apache.doris.nereids.rules.rewrite.AggScalarSubQueryToWindowFunction;
@@ -762,10 +762,10 @@ public class Rewriter extends AbstractBatchJobExecutor {
                     topDown(new SumLiteralRewrite(),
                             new MergePercentileToArray())
                 ),
-                topic("add projection for unique function",
-                        // separate AddProjectForUniqueFunction and 
MergeProjectable
+                topic("add projection for volatile expression",
+                        // separate AddProjectForVolatileExpression and 
MergeProjectable
                         // to avoid dead loop if code has bug
-                        topDown(new AddProjectForUniqueFunction()),
+                        topDown(new AddProjectForVolatileExpression()),
                         topDown(new MergeProjectable())
                 ),
                 topic("collect scan filter for hbo",
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 099f07418d8..71e7514025e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -229,7 +229,7 @@ public enum RuleType {
     PUSH_DOWN_DISTINCT_THROUGH_JOIN(RuleTypeClass.REWRITE),
 
     ADD_PROJECT_FOR_JOIN(RuleTypeClass.REWRITE),
-    ADD_PROJECT_FOR_UNIQUE_FUNCTION(RuleTypeClass.REWRITE),
+    ADD_PROJECT_FOR_VOLATILE_EXPRESSION(RuleTypeClass.REWRITE),
     VARIANT_SUB_PATH_PRUNING(RuleTypeClass.REWRITE),
     NESTED_COLUMN_PRUNING(RuleTypeClass.REWRITE),
     CLEAR_CONTEXT_STATUS(RuleTypeClass.REWRITE),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
index a38fdb3edbd..d367d093c36 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindExpression.java
@@ -1434,7 +1434,8 @@ public class BindExpression implements 
AnalysisRuleFactory {
         for (Expression groupByExpr : groupByExpressions) {
             Expression newGroupByExpr = groupByExpr;
             if (groupByExpr.containsVolatileExpression()) {
-                Expression ignoreUniqueIdExpr = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(groupByExpr, true);
+                Expression ignoreUniqueIdExpr = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(groupByExpr,
+                        true);
                 Expression previousGroupByExpr = 
ignoreUniqueIdGroupByExprs.get(ignoreUniqueIdExpr);
                 if (previousGroupByExpr == null) {
                     ignoreUniqueIdGroupByExprs.put(ignoreUniqueIdExpr, 
groupByExpr);
@@ -1481,10 +1482,10 @@ public class BindExpression implements 
AnalysisRuleFactory {
         }
 
         // first ignore unique id, then replace sub expression with group by 
expression
-        Expression resExpr = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(expression, true);
+        Expression resExpr = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(expression, true);
         resExpr = ExpressionUtils.replace(resExpr, bindUniqueIdReplaceMap);
         // enable unique id back
-        resExpr = ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(resExpr, 
false);
+        resExpr = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(resExpr, false);
         return (T) resExpr;
     }
 
@@ -1523,7 +1524,8 @@ public class BindExpression implements 
AnalysisRuleFactory {
         Map<Expression, Expression> replaceMap = Maps.newHashMap();
         for (Expression expression : groupByByExpressions) {
             if (expression.containsVolatileExpression()) {
-                Expression ignoreUniqueIdExpr = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(expression, true);
+                Expression ignoreUniqueIdExpr = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(expression,
+                        true);
                 // for sql:
                 //    select distinct a + random(),  a + random()
                 //    from t
@@ -1555,7 +1557,8 @@ public class BindExpression implements 
AnalysisRuleFactory {
             for (Expression groupBy : boundGroupingSet) {
                 Expression newGroupBy = groupBy;
                 if (groupBy.containsVolatileExpression()) {
-                    Expression ignoreUniqueIdGroupBy = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(groupBy, true);
+                    Expression ignoreUniqueIdGroupBy = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(groupBy,
+                            true);
                     Expression previousGroupBy = 
ignoreUniqueIdGroupByExpressions.get(ignoreUniqueIdGroupBy);
                     if (previousGroupBy == null) {
                         
ignoreUniqueIdGroupByExpressions.put(ignoreUniqueIdGroupBy, groupBy);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpression.java
similarity index 86%
rename from 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunction.java
rename to 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpression.java
index 97fc2a671c4..7f3f0cd7684 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpression.java
@@ -27,7 +27,6 @@ import 
org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
 import org.apache.doris.nereids.trees.expressions.functions.Function;
-import 
org.apache.doris.nereids.trees.expressions.functions.scalar.UniqueFunction;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
@@ -53,12 +52,12 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 
-/** extract unique function expression which exist multiple times, and add 
them to a new project child.
+/** extract volatile expression which exist multiple times, and add them to a 
new project child.
  * for example:
  * before rewrite:  filter(random() >= 5 and random() <= 10), suppose the two 
random have the same unique expr id.
  * after rewrite: filter(k >= 5 and k <= 10) -> project(random() as k)
  */
-public class AddProjectForUniqueFunction implements RewriteRuleFactory {
+public class AddProjectForVolatileExpression implements RewriteRuleFactory {
 
     @Override
     public List<Rule> buildRules() {
@@ -86,7 +85,7 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                 } else {
                     return generate;
                 }
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -95,13 +94,14 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
         public Rule build() {
             return logicalOneRowRelation().thenApply(ctx -> {
                 LogicalOneRowRelation oneRowRelation = ctx.root;
-                List<NamedExpression> uniqueFunctionAlias = 
tryGenUniqueFunctionAlias(oneRowRelation.getProjects());
-                if (uniqueFunctionAlias.isEmpty()) {
+                List<NamedExpression> volatileExpressionAlias =
+                        
tryGenVolatileExpressionAlias(oneRowRelation.getProjects());
+                if (volatileExpressionAlias.isEmpty()) {
                     return oneRowRelation;
                 }
 
                 Map<Expression, Slot> replaceMap = Maps.newHashMap();
-                for (NamedExpression alias : uniqueFunctionAlias) {
+                for (NamedExpression alias : volatileExpressionAlias) {
                     replaceMap.put(alias.child(0), alias.toSlot());
                 }
                 ImmutableList.Builder<NamedExpression> newProjectBuilder
@@ -111,8 +111,8 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                 }
                 return new LogicalProject<>(
                         newProjectBuilder.build(),
-                        oneRowRelation.withProjects(uniqueFunctionAlias));
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+                        oneRowRelation.withProjects(volatileExpressionAlias));
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -128,7 +128,7 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                 } else {
                     return project;
                 }
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -146,7 +146,7 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                 } else {
                     return filter;
                 }
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -163,7 +163,7 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                 } else {
                     return having;
                 }
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -192,7 +192,7 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                     newOutputBuilder.add((NamedExpression) newTargets.get(i));
                 }
                 return aggregate.withChildGroupByAndOutput(newGroupBy, 
newOutputBuilder.build(), newChild);
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
@@ -235,30 +235,30 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
                         join.getMarkJoinSlotReference(),
                         ImmutableList.of(newLeftChild, join.right()),
                         join.getJoinReorderContext());
-            }).toRule(RuleType.ADD_PROJECT_FOR_UNIQUE_FUNCTION);
+            }).toRule(RuleType.ADD_PROJECT_FOR_VOLATILE_EXPRESSION);
         }
     }
 
     /**
-     * extract unique function which exist multiple times from targets,
-     * then alias the unique function and put them into a child project,
+     * extract volatile expression which exist multiple times from targets,
+     * then alias the volatile expression and put them into a child project,
      * then rewrite targets with the alias names.
      */
     @VisibleForTesting
     public <T extends Expression> Optional<Pair<List<T>, 
LogicalProject<Plan>>> rewriteExpressions(
             LogicalPlan plan, Collection<T> targets) {
-        List<NamedExpression> uniqueFunctionAlias = 
tryGenUniqueFunctionAlias(targets);
-        if (uniqueFunctionAlias.isEmpty()) {
+        List<NamedExpression> volatileExpressionAlias = 
tryGenVolatileExpressionAlias(targets);
+        if (volatileExpressionAlias.isEmpty()) {
             return Optional.empty();
         }
 
         List<NamedExpression> projects = 
ImmutableList.<NamedExpression>builder()
                 .addAll(plan.child(0).getOutputSet())
-                .addAll(uniqueFunctionAlias)
+                .addAll(volatileExpressionAlias)
                 .build();
 
         Map<Expression, Slot> replaceMap = Maps.newHashMap();
-        for (NamedExpression alias : uniqueFunctionAlias) {
+        for (NamedExpression alias : volatileExpressionAlias) {
             replaceMap.put(alias.child(0), alias.toSlot());
         }
         ImmutableList.Builder<T> newTargetsBuilder = 
ImmutableList.builderWithExpectedSize(targets.size());
@@ -270,23 +270,23 @@ public class AddProjectForUniqueFunction implements 
RewriteRuleFactory {
     }
 
     /**
-     * if a unique function exists multiple times in the targets, then add a 
project to alias it.
+     * if a volatile expression exists multiple times in the targets, then add 
a project to alias it.
      */
     @VisibleForTesting
-    public List<NamedExpression> tryGenUniqueFunctionAlias(Collection<? 
extends Expression> targets) {
-        Map<Expression, Integer> unqiueFunctionCounter = 
Maps.newLinkedHashMap();
+    public List<NamedExpression> tryGenVolatileExpressionAlias(Collection<? 
extends Expression> targets) {
+        Map<Expression, Integer> volatileExpressionCounter = 
Maps.newLinkedHashMap();
         for (Expression target : targets) {
             target.foreach(e -> {
                 Expression expr = (Expression) e;
-                if (expr instanceof UniqueFunction) {
-                    unqiueFunctionCounter.merge((UniqueFunction) expr, 1, 
Integer::sum);
+                if (expr.isVolatile()) {
+                    volatileExpressionCounter.merge(expr, 1, Integer::sum);
                 }
             });
         }
 
         ImmutableList.Builder<NamedExpression> builder
-                = 
ImmutableList.builderWithExpectedSize(unqiueFunctionCounter.size());
-        for (Entry<Expression, Integer> entry : 
unqiueFunctionCounter.entrySet()) {
+                = 
ImmutableList.builderWithExpectedSize(volatileExpressionCounter.size());
+        for (Entry<Expression, Integer> entry : 
volatileExpressionCounter.entrySet()) {
             if (entry.getValue() > 1) {
                 ExprId exprId = StatementScopeIdGenerator.newExprId();
                 String functionName = entry.getKey() instanceof Function
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
index abda37ff0b4..cca16d06639 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/MergeOneRowRelationIntoUnion.java
@@ -46,10 +46,10 @@ public class MergeOneRowRelationIntoUnion extends 
OneRewriteRuleFactory {
                     ImmutableList.Builder<List<SlotReference>> 
newChildrenOutputs = ImmutableList.builder();
                     for (int i = 0; i < u.arity(); i++) {
                         Plan child = u.child(i);
-                        // if one row relation contains unique function which 
exist multiple times,
-                        // don't merge it, later AddProjectForUniqueFunction 
will handle this one row relation.
+                        // if one row relation contains volatile expression 
which exist multiple times,
+                        // don't merge it, later 
AddProjectForVolatileExpression will handle this one row relation.
                         if (!(child instanceof LogicalOneRowRelation)
-                                || 
ExpressionUtils.containUniqueFunctionExistMultiple(
+                                || 
ExpressionUtils.containVolatileExpressionExistMultiple(
                                         ((LogicalOneRowRelation) 
child).getProjects())) {
                             newChildren.add(child);
                             newChildrenOutputs.add(u.getRegularChildOutput(i));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownVirtualColumnsIntoOlapScan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownVirtualColumnsIntoOlapScan.java
index 624fb936ea6..4c60a9dade3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownVirtualColumnsIntoOlapScan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownVirtualColumnsIntoOlapScan.java
@@ -338,7 +338,7 @@ public class PushDownVirtualColumnsIntoOlapScan implements 
RewriteRuleFactory {
         if (!(skipResult.shouldSkipCounting() || 
skipResult.isNotBeneficial())) {
             if (expr.getDepth() >= MIN_EXPRESSION_DEPTH
                     && expr.children().size() > 0
-                    && 
!ExpressionUtils.containUniqueFunctionExistMultiple(ImmutableList.of(expr))) {
+                    && 
!ExpressionUtils.containVolatileExpressionExistMultiple(ImmutableList.of(expr)))
 {
                 expressionCounts.put(expr, expressionCounts.getOrDefault(expr, 
0) + 1);
             }
         }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
index ce3edfbeb32..fabadac3ffe 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
@@ -387,11 +387,6 @@ public abstract class Expression extends 
AbstractTreeNode<Expression> implements
                 && ((SlotReference) this).getOriginalColumn().get().isKey();
     }
 
-    public boolean containsVolatileExpression() {
-        return anyMatch(expr -> expr instanceof VolatileExpression
-                && ((VolatileExpression) expr).isVolatile());
-    }
-
     /** containsNullLiteralChildren */
     public boolean containsNullLiteralChildren() {
         return getOrInitMutableState("CONTAINS_NULL_LITERAL_CHILDREN", () -> {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/VolatileExpression.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/VolatileExpression.java
index d43d326b6a3..3310e1e07ba 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/VolatileExpression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/VolatileExpression.java
@@ -17,12 +17,15 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
+
 /** Expression that may carry per-call volatile identity for optimizer rewrite 
safety. */
-public interface VolatileExpression {
+public interface VolatileExpression extends ExpressionTrait {
     VolatileIdentity getVolatileIdentity();
 
     Expression withIgnoreUniqueId(boolean ignoreUniqueId);
 
+    @Override
     default boolean isVolatile() {
         return getVolatileIdentity().isVolatile();
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
index a8d9c8dbb69..f382964bcaf 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.TreeNode;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Variable;
+import org.apache.doris.nereids.trees.expressions.VolatileExpression;
 import org.apache.doris.nereids.types.DataType;
 
 import com.google.common.collect.ImmutableList;
@@ -126,4 +127,16 @@ public interface ExpressionTrait extends 
TreeNode<Expression> {
     default boolean containsNondeterministic() {
         return anyMatch(expr -> !((ExpressionTrait) expr).isDeterministic());
     }
+
+    /**
+     * Identify whether the expression itself needs volatile identity.
+     * Only VolatileExpression is allowed to override this method.
+     */
+    default boolean isVolatile() {
+        return false;
+    }
+
+    default boolean containsVolatileExpression() {
+        return containsType(VolatileExpression.class) && anyMatch(expr -> 
((ExpressionTrait) expr).isVolatile());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java
index d35b1dd9ef8..952c34bbc10 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java
@@ -19,9 +19,8 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BigIntType;
@@ -51,37 +50,25 @@ public class Random extends UniqueFunction
      * constructor with 0 argument.
      */
     public Random() {
-        this(StatementScopeIdGenerator.newExprId(), false);
+        this(VolatileIdentity.newVolatileIdentity(), ImmutableList.of());
     }
 
     /**
      * constructor with 1 argument.
      */
     public Random(Expression arg) {
-        this(StatementScopeIdGenerator.newExprId(), false, arg);
+        this(VolatileIdentity.newVolatileIdentity(), ImmutableList.of(arg));
     }
 
     /**
      * constructor with 2 argument.
      */
     public Random(Expression lchild, Expression rchild) {
-        this(StatementScopeIdGenerator.newExprId(), false, lchild, rchild);
+        this(VolatileIdentity.newVolatileIdentity(), ImmutableList.of(lchild, 
rchild));
     }
 
-    public Random(ExprId uniqueId, boolean ignoreUniqueId) {
-        super("random", uniqueId, ignoreUniqueId);
-    }
-
-    public Random(ExprId uniqueId, boolean ignoreUniqueId, Expression arg) {
-        super("random", uniqueId, ignoreUniqueId, arg);
-    }
-
-    public Random(ExprId uniqueId, boolean ignoreUniqueId, Expression lchild, 
Expression rchild) {
-        super("random", uniqueId, ignoreUniqueId, lchild, rchild);
-    }
-
-    private Random(ExprId uniqueId, boolean ignoreUniqueId, List<Expression> 
children) {
-        super("random", uniqueId, ignoreUniqueId, children);
+    private Random(VolatileIdentity volatileIdentity, List<Expression> 
children) {
+        super("random", volatileIdentity, children);
     }
 
     /** constructor for withChildren and reuse signature */
@@ -125,7 +112,7 @@ public class Random extends UniqueFunction
 
     @Override
     public Random withIgnoreUniqueId(boolean ignoreUniqueId) {
-        return new Random(uniqueId, ignoreUniqueId, children);
+        return new Random(volatileIdentity.withIgnoreUniqueId(ignoreUniqueId), 
children);
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RandomBytes.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RandomBytes.java
index c185f866c0e..d45558a02bc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RandomBytes.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RandomBytes.java
@@ -18,9 +18,8 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -48,11 +47,11 @@ public class RandomBytes extends UniqueFunction
      * constructor with 1 argument.
      */
     public RandomBytes(Expression arg0) {
-        this(StatementScopeIdGenerator.newExprId(), false, arg0);
+        this(VolatileIdentity.newVolatileIdentity(), ImmutableList.of(arg0));
     }
 
-    public RandomBytes(ExprId uniqueId, boolean ignoreUniqueId, Expression 
arg0) {
-        super("random_bytes", uniqueId, ignoreUniqueId, arg0);
+    private RandomBytes(VolatileIdentity volatileIdentity, List<Expression> 
children) {
+        super("random_bytes", volatileIdentity, children);
     }
 
     /** constructor for withChildren and reuse signature */
@@ -71,7 +70,7 @@ public class RandomBytes extends UniqueFunction
 
     @Override
     public RandomBytes withIgnoreUniqueId(boolean ignoreUniqueId) {
-        return new RandomBytes(uniqueId, ignoreUniqueId, children.get(0));
+        return new 
RandomBytes(volatileIdentity.withIgnoreUniqueId(ignoreUniqueId), children);
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunction.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunction.java
index 83875fd2dce..622aa790ac7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunction.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunction.java
@@ -17,11 +17,13 @@
 
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.VolatileExpression;
 import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
 import java.util.List;
 
 /**
@@ -100,31 +102,22 @@ import java.util.List;
 public abstract class UniqueFunction extends ScalarFunction implements 
VolatileExpression {
 
     protected final VolatileIdentity volatileIdentity;
-    protected final ExprId uniqueId;
-
-    // when compare and bind unique id with group by expressions, should 
ignore the unique id
-    protected final boolean ignoreUniqueId;
 
     /** constructor for withChildren and reuse signature */
     public UniqueFunction(UniqueFunctionParams functionParams) {
         super(functionParams);
-        this.volatileIdentity = VolatileIdentity.of(functionParams.uniqueId, 
functionParams.ignoreUniqueId);
-        this.uniqueId = functionParams.uniqueId;
-        this.ignoreUniqueId = functionParams.ignoreUniqueId;
+        this.volatileIdentity = functionParams.volatileIdentity;
+        checkVolatileIdentity();
     }
 
-    public UniqueFunction(String name, ExprId uniqueId, boolean 
ignoreUniqueId, Expression... arguments) {
-        super(name, arguments);
-        this.volatileIdentity = VolatileIdentity.of(uniqueId, ignoreUniqueId);
-        this.uniqueId = uniqueId;
-        this.ignoreUniqueId = ignoreUniqueId;
+    public UniqueFunction(String name, VolatileIdentity volatileIdentity, 
Expression... arguments) {
+        this(name, volatileIdentity, ImmutableList.copyOf(arguments));
     }
 
-    public UniqueFunction(String name, ExprId uniqueId, boolean 
ignoreUniqueId, List<Expression> arguments) {
+    public UniqueFunction(String name, VolatileIdentity volatileIdentity, 
List<Expression> arguments) {
         super(name, arguments);
-        this.volatileIdentity = VolatileIdentity.of(uniqueId, ignoreUniqueId);
-        this.uniqueId = uniqueId;
-        this.ignoreUniqueId = ignoreUniqueId;
+        this.volatileIdentity = volatileIdentity;
+        checkVolatileIdentity();
     }
 
     @Override
@@ -132,11 +125,16 @@ public abstract class UniqueFunction extends 
ScalarFunction implements VolatileE
         return volatileIdentity;
     }
 
+    @Override
     public abstract UniqueFunction withIgnoreUniqueId(boolean ignoreUniqueId);
 
     @Override
     protected UniqueFunctionParams getFunctionParams(List<Expression> 
arguments) {
-        return new UniqueFunctionParams(this, getName(), uniqueId, 
ignoreUniqueId, arguments, isInferred());
+        return new UniqueFunctionParams(this, getName(), volatileIdentity, 
arguments, isInferred());
+    }
+
+    private void checkVolatileIdentity() {
+        Preconditions.checkArgument(volatileIdentity.isVolatile(), 
"UniqueFunction must have a volatile identity");
     }
 
     @Override
@@ -158,15 +156,14 @@ public abstract class UniqueFunction extends 
ScalarFunction implements VolatileE
             return false;
         }
         UniqueFunction other = (UniqueFunction) o;
-        // in BindExpression phase, when compare two expression equals except 
the unique id,
+        // in BindExpression phase, when compare two expression equals except 
the volatile identity,
         // will set ignoreUniqueId = true temporarily, after bind expression, 
will recover ignoreUniqueId = false
         return volatileIdentity.equalsByIdentity(other.volatileIdentity, 
super.equals(other));
     }
 
-    // The contains method needs to use hashCode, so similar to equals, it 
only compares exprId
+    // The contains method needs to use hashCode, so similar to equals, it 
only compares volatile identity
     @Override
     public int computeHashCode() {
-        // direct return exprId to speed up
         return volatileIdentity.hashCodeByIdentity(super.computeHashCode());
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunctionParams.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunctionParams.java
index 7030eefad4a..1dacc5c2884 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunctionParams.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UniqueFunctionParams.java
@@ -17,8 +17,8 @@
 
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 
 import org.jetbrains.annotations.Nullable;
 
@@ -27,18 +27,11 @@ import java.util.List;
 /** UniqueFunctionParams */
 public class UniqueFunctionParams extends ScalarFunctionParams {
 
-    public final ExprId uniqueId;
-    public final boolean ignoreUniqueId;
+    public final VolatileIdentity volatileIdentity;
 
-    public UniqueFunctionParams(String functionName, ExprId uniqueId, boolean 
ignoreUniqueId,
-            List<Expression> arguments) {
-        this(null, functionName, uniqueId, ignoreUniqueId, arguments, false);
-    }
-
-    public UniqueFunctionParams(@Nullable UniqueFunction originFunction, 
String functionName, ExprId uniqueId,
-            boolean ignoreUniqueId, List<Expression> arguments, boolean 
inferred) {
+    public UniqueFunctionParams(@Nullable UniqueFunction originFunction, 
String functionName,
+            VolatileIdentity volatileIdentity, List<Expression> arguments, 
boolean inferred) {
         super(originFunction, functionName, arguments, inferred);
-        this.uniqueId = uniqueId;
-        this.ignoreUniqueId = ignoreUniqueId;
+        this.volatileIdentity = volatileIdentity;
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uuid.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uuid.java
index f71ff42b281..37de9d2cc2c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uuid.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uuid.java
@@ -18,9 +18,8 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
@@ -46,11 +45,11 @@ public class Uuid extends UniqueFunction
      * constructor with 0 argument.
      */
     public Uuid() {
-        this(StatementScopeIdGenerator.newExprId(), false);
+        this(VolatileIdentity.newVolatileIdentity());
     }
 
-    public Uuid(ExprId uniqueId, boolean ignoreUniqueId) {
-        super("uuid", uniqueId, ignoreUniqueId);
+    private Uuid(VolatileIdentity volatileIdentity) {
+        super("uuid", volatileIdentity);
     }
 
     /** constructor for withChildren and reuse signature */
@@ -66,7 +65,7 @@ public class Uuid extends UniqueFunction
 
     @Override
     public Uuid withIgnoreUniqueId(boolean ignoreUniqueId) {
-        return new Uuid(uniqueId, ignoreUniqueId);
+        return new Uuid(volatileIdentity.withIgnoreUniqueId(ignoreUniqueId));
     }
 
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UuidNumeric.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UuidNumeric.java
index e95bf46e069..d71f9a803d8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UuidNumeric.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UuidNumeric.java
@@ -18,9 +18,8 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
@@ -46,11 +45,11 @@ public class UuidNumeric extends UniqueFunction
      * constructor with 0 argument.
      */
     public UuidNumeric() {
-        this(StatementScopeIdGenerator.newExprId(), false);
+        this(VolatileIdentity.newVolatileIdentity());
     }
 
-    public UuidNumeric(ExprId uniqueId, boolean ignoreUniqueId) {
-        super("uuid_numeric", uniqueId, ignoreUniqueId);
+    private UuidNumeric(VolatileIdentity volatileIdentity) {
+        super("uuid_numeric", volatileIdentity);
     }
 
     /** constructor for withChildren and reuse signature */
@@ -66,7 +65,7 @@ public class UuidNumeric extends UniqueFunction
 
     @Override
     public UuidNumeric withIgnoreUniqueId(boolean ignoreUniqueId) {
-        return new UuidNumeric(uniqueId, ignoreUniqueId);
+        return new 
UuidNumeric(volatileIdentity.withIgnoreUniqueId(ignoreUniqueId));
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
index bd1eb02ab98..e594556eda3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -643,11 +643,11 @@ public class ExpressionUtils {
     }
 
     /**
-     * set ignore unique id for unique functions
+     * Set ignore unique id for volatile expressions.
      */
-    public static Expression setIgnoreUniqueIdForUniqueFunc(Expression 
expression, boolean ignoreUniqueId) {
+    public static Expression setIgnoreUniqueIdForVolatileExpression(Expression 
expression, boolean ignoreUniqueId) {
         return expression.rewriteDownShortCircuit(e ->
-                e instanceof VolatileExpression && ((VolatileExpression) 
e).isVolatile()
+                e.isVolatile()
                         ? ((VolatileExpression) 
e).withIgnoreUniqueId(ignoreUniqueId) : e);
     }
 
@@ -1344,15 +1344,13 @@ public class ExpressionUtils {
     }
 
     /**
-     * check if the expressions contain a unique function which exists 
multiple times
+     * check if the expressions contain a volatile expression which exists 
multiple times
      */
-    public static boolean containUniqueFunctionExistMultiple(Collection<? 
extends Expression> expressions) {
+    public static boolean containVolatileExpressionExistMultiple(Collection<? 
extends Expression> expressions) {
         Set<Expression> counterSet = Sets.newHashSet();
         for (Expression expression : expressions) {
             if (expression.anyMatch(
-                    expr -> expr instanceof VolatileExpression
-                            && ((VolatileExpression) expr).isVolatile()
-                            && !counterSet.add((Expression) expr))) {
+                    expr -> ((Expression) expr).isVolatile() && 
!counterSet.add((Expression) expr))) {
                 return true;
             }
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunctionTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpressionTest.java
similarity index 78%
rename from 
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunctionTest.java
rename to 
fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpressionTest.java
index 715c4c3f1c5..48b5bc8ad2b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForUniqueFunctionTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/AddProjectForVolatileExpressionTest.java
@@ -17,6 +17,9 @@
 
 package org.apache.doris.nereids.rules.rewrite;
 
+import org.apache.doris.catalog.Function.NullableMode;
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.catalog.FunctionVolatility;
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.hint.DistributeHint;
 import org.apache.doris.nereids.trees.expressions.Add;
@@ -26,14 +29,18 @@ import 
org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.VolatileIdentity;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Random;
+import org.apache.doris.nereids.trees.expressions.functions.udf.JavaUdf;
 import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.plans.DistributeType;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.util.MemoPatternMatchSupported;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PlanChecker;
@@ -46,12 +53,12 @@ import org.junit.jupiter.api.Test;
 import java.util.List;
 import java.util.Optional;
 
-public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupported {
+public class AddProjectForVolatileExpressionTest implements 
MemoPatternMatchSupported {
     private final LogicalOlapScan studentOlapScan
             = new LogicalOlapScan(StatementScopeIdGenerator.newRelationId(), 
PlanConstructor.student);
 
     @Test
-    void testGenUniqueFunctionAlias() {
+    void testGenVolatileExpressionAlias() {
         Random random1 = new Random();
         Random random2 = new Random();
         Random random3 = new Random();
@@ -59,7 +66,8 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
                 new Add(random1, new Add(random1, new DoubleLiteral(1.0))),
                 new Add(random2, random3),
                 random3);
-        List<NamedExpression> namedExpressions = new 
AddProjectForUniqueFunction().tryGenUniqueFunctionAlias(expressions);
+        List<NamedExpression> namedExpressions = new 
AddProjectForVolatileExpression()
+                .tryGenVolatileExpressionAlias(expressions);
         Assertions.assertEquals(2, namedExpressions.size());
         Assertions.assertInstanceOf(Alias.class, namedExpressions.get(0));
         Assertions.assertEquals(((Alias) namedExpressions.get(0)).child(), 
random1);
@@ -67,6 +75,22 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
         Assertions.assertEquals(((Alias) namedExpressions.get(1)).child(), 
random3);
     }
 
+    @Test
+    void testGenVolatileUdfAlias() {
+        JavaUdf volatileUdf = javaUdf(FunctionVolatility.VOLATILE, 
VolatileIdentity.newVolatileIdentity());
+        JavaUdf stableUdf = javaUdf(FunctionVolatility.STABLE, 
VolatileIdentity.NON_VOLATILE);
+        List<Expression> expressions = ImmutableList.of(
+                new Add(volatileUdf, new IntegerLiteral(1)),
+                volatileUdf,
+                new Add(stableUdf, stableUdf));
+
+        List<NamedExpression> namedExpressions = new 
AddProjectForVolatileExpression()
+                .tryGenVolatileExpressionAlias(expressions);
+        Assertions.assertEquals(1, namedExpressions.size());
+        Assertions.assertInstanceOf(Alias.class, namedExpressions.get(0));
+        Assertions.assertEquals(((Alias) namedExpressions.get(0)).child(), 
volatileUdf);
+    }
+
     @Test
     void testRewriteExpressionNoChange() {
         Random random1 = new Random();
@@ -77,7 +101,7 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
                 new Alias(new Add(random2, new DoubleLiteral(1.0))),
                 new Alias(random3));
         LogicalProject<?> project = new LogicalProject<Plan>(projections, 
studentOlapScan);
-        Optional<Pair<List<NamedExpression>, LogicalProject<Plan>>> result = 
new AddProjectForUniqueFunction()
+        Optional<Pair<List<NamedExpression>, LogicalProject<Plan>>> result = 
new AddProjectForVolatileExpression()
                 .rewriteExpressions(project, project.getProjects());
         Assertions.assertEquals(Optional.empty(), result);
     }
@@ -91,7 +115,7 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
                 new Alias(new Add(random2, new DoubleLiteral(1.0))),
                 new Alias(random2));
         LogicalProject<?> project = new LogicalProject<Plan>(projections, 
studentOlapScan);
-        Optional<Pair<List<NamedExpression>, LogicalProject<Plan>>> result = 
new AddProjectForUniqueFunction()
+        Optional<Pair<List<NamedExpression>, LogicalProject<Plan>>> result = 
new AddProjectForVolatileExpression()
                 .rewriteExpressions(project, project.getProjects());
         Assertions.assertTrue(result.isPresent());
         Assertions.assertInstanceOf(LogicalProject.class, result.get().second);
@@ -125,7 +149,7 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
                 null);
 
         Plan root = PlanChecker.from(MemoTestUtils.createConnectContext(), 
join)
-                .applyTopDown(new AddProjectForUniqueFunction())
+                .applyTopDown(new AddProjectForVolatileExpression())
                 .getPlan();
         Assertions.assertInstanceOf(LogicalJoin.class, root);
         LogicalJoin<?, ?> newJoin = (LogicalJoin<?, ?>) root;
@@ -140,4 +164,11 @@ public class AddProjectForUniqueFunctionTest implements 
MemoPatternMatchSupporte
         Assertions.assertEquals(ImmutableList.of(new EqualTo(alias.toSlot(), 
new DoubleLiteral(1.0))), newJoin.getMarkJoinConjuncts());
         Assertions.assertEquals(JoinType.INNER_JOIN, newJoin.getJoinType());
     }
+
+    private JavaUdf javaUdf(FunctionVolatility volatility, VolatileIdentity 
volatileIdentity) {
+        return new JavaUdf("java_fn", 1, "db1", 
org.apache.doris.catalog.Function.BinaryType.JAVA_UDF,
+                
FunctionSignature.ret(IntegerType.INSTANCE).args(IntegerType.INSTANCE),
+                NullableMode.ALWAYS_NULLABLE, volatility, volatileIdentity,
+                null, "evaluate", null, null, "", false, 360, new 
IntegerLiteral(1));
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/udf/UdfVolatilityTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/udf/UdfVolatilityTest.java
index 89fd24821ba..290650435e9 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/udf/UdfVolatilityTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/udf/UdfVolatilityTest.java
@@ -50,8 +50,8 @@ class UdfVolatilityTest {
         Assertions.assertTrue(first.containsVolatileExpression());
         Assertions.assertNotEquals(first, second);
 
-        Expression ignoredFirst = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(first, true);
-        Expression ignoredSecond = 
ExpressionUtils.setIgnoreUniqueIdForUniqueFunc(second, true);
+        Expression ignoredFirst = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(first, true);
+        Expression ignoredSecond = 
ExpressionUtils.setIgnoreUniqueIdForVolatileExpression(second, true);
         Assertions.assertEquals(ignoredFirst, ignoredSecond);
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to