You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2023/06/06 06:35:30 UTC

[doris] branch master updated: [feature](planner)(nereids) support user defined variable (#20334)

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

morrysnow 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 ae428c29e2 [feature](planner)(nereids) support user defined variable (#20334)
ae428c29e2 is described below

commit ae428c29e23b6df7822e943d70ad7acd291412b9
Author: Chengpeng Yan <41...@users.noreply.github.com>
AuthorDate: Tue Jun 6 14:35:16 2023 +0800

    [feature](planner)(nereids) support user defined variable (#20334)
    
    Support user-defined variables.
    After this PR, we can use `set @a = xx` to define a user variable and use it in the query like `select @a`.
    
    the changes of this PR:
    1. Support the grammar for `set user variable` in the parser.
    2. Add the `userVars` in `VariableMgr` to store the user-defined variables.
    3. For the `set @a = xx`, we will store the variable name and its value in the `userVars` in `VariableMgr`.
    4. For the `select @a`, we will get the value for the variable name in `userVars`.
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  2 +-
 fe/fe-core/src/main/cup/sql_parser.cup             | 12 ++--
 .../main/java/org/apache/doris/analysis/Expr.java  |  3 +-
 .../apache/doris/analysis/ExpressionFunctions.java |  6 +-
 .../java/org/apache/doris/analysis/SetType.java    |  3 +-
 .../{SetType.java => SetUserDefinedVar.java}       | 49 ++++---------
 .../java/org/apache/doris/analysis/SetVar.java     | 37 ++++++++--
 .../{SysVariableDesc.java => VariableExpr.java}    | 66 ++++++++++++------
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  3 +-
 .../nereids/trees/expressions/literal/Literal.java |  7 ++
 .../main/java/org/apache/doris/qe/SetExecutor.java |  3 +
 .../main/java/org/apache/doris/qe/VariableMgr.java | 80 ++++++++++++++++++++--
 .../apache/doris/rewrite/FoldConstantsRule.java    | 14 ++--
 .../java/org/apache/doris/analysis/SetVarTest.java |  2 +-
 ...VariableDescTest.java => VariableExprTest.java} |  6 +-
 .../java/org/apache/doris/qe/VariableMgrTest.java  |  4 +-
 regression-test/data/nereids_p0/test_user_var.out  | 16 +++++
 .../data/query_p0/set/test_user_var.out            | 16 +++++
 .../suites/nereids_p0/test_user_var.groovy         | 32 +++++++++
 .../suites/query_p0/set/test_user_var.groovy       | 30 ++++++++
 20 files changed, 299 insertions(+), 92 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 14ad9c35c4..c8035e716e 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -342,7 +342,7 @@ primaryExpression
       (COMMA arguments+=expression)* (ORDER BY sortItem (COMMA sortItem)*)?)? RIGHT_PAREN
       (OVER windowSpec)?                                                                        #functionCall
     | LEFT_PAREN query RIGHT_PAREN                                                             #subqueryExpression
-    | ATSIGN identifier                                                                        #userVariable
+    | ATSIGN identifierOrText                                                                        #userVariable
     | DOUBLEATSIGN (kind=(GLOBAL | SESSION) DOT)? identifier                                     #systemVariable
     | identifier                                                                               #columnReference
     | base=primaryExpression DOT fieldName=identifier                                          #dereference
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup
index 3a85b99dd4..4f2014b001 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -4914,9 +4914,9 @@ option_value_no_option_type ::=
     {:
         RESULT = new SetVar(variable, expr);
     :}
-    | AT ident_or_text:var equal literal:expr
+    | AT ident_or_text:var equal expr:expr
     {:
-        RESULT = new SetVar(var, expr);
+        RESULT = new SetUserDefinedVar(var, expr);
     :}
     /* Ident */
     | AT AT variable_name:variable equal set_expr_or_default:expr
@@ -6398,13 +6398,17 @@ exists_predicate ::=
 non_pred_expr ::=
   sign_chain_expr:e
   {: RESULT = e; :}
+  | AT ident:l
+  {:
+    RESULT = new VariableExpr(l, SetType.USER);
+  :}
   | AT AT ident:l
   {:
-    RESULT = new SysVariableDesc(l);
+    RESULT = new VariableExpr(l);
   :}
   | AT AT var_ident_type:type ident:l
   {:
-    RESULT = new SysVariableDesc(l, type);
+    RESULT = new VariableExpr(l, type);
   :}
   | literal:l
   {: RESULT = l; :}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
index a6cc659794..8633b868eb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Expr.java
@@ -990,7 +990,8 @@ public abstract class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl
             // Hack to ensure BE never sees TYPE_NULL. If an expr makes it this far without
             // being cast to a non-NULL type, the type doesn't matter and we can cast it
             // arbitrarily.
-            Preconditions.checkState(this instanceof NullLiteral || this instanceof SlotRef);
+            Preconditions.checkState(this instanceof NullLiteral || this instanceof SlotRef
+                    || this instanceof VariableExpr);
             return NullLiteral.create(ScalarType.BOOLEAN).treeToThrift();
         }
         TExpr result = new TExpr();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ExpressionFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ExpressionFunctions.java
index e7dae9617b..e39748b570 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ExpressionFunctions.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ExpressionFunctions.java
@@ -58,7 +58,7 @@ public enum ExpressionFunctions {
     public Expr evalExpr(Expr constExpr) {
         // Function's arg are all LiteralExpr.
         for (Expr child : constExpr.getChildren()) {
-            if (!(child instanceof LiteralExpr) && !(child instanceof SysVariableDesc)) {
+            if (!(child instanceof LiteralExpr) && !(child instanceof VariableExpr)) {
                 return constExpr;
             }
         }
@@ -115,8 +115,8 @@ public enum ExpressionFunctions {
                     return constExpr;
                 }
             }
-        } else if (constExpr instanceof SysVariableDesc) {
-            return ((SysVariableDesc) constExpr).getLiteralExpr();
+        } else if (constExpr instanceof VariableExpr) {
+            return ((VariableExpr) constExpr).getLiteralExpr();
         }
         return constExpr;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java
index 924c1f38a9..ae0fbd27bc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java
@@ -23,7 +23,8 @@ import org.apache.doris.thrift.TVarType;
 public enum SetType {
     DEFAULT("DEFAULT"),
     GLOBAL("GLOBAL"),
-    SESSION("SESSION");
+    SESSION("SESSION"),
+    USER("USER");
 
     private String desc;
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetUserDefinedVar.java
similarity index 53%
copy from fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java
copy to fe/fe-core/src/main/java/org/apache/doris/analysis/SetUserDefinedVar.java
index 924c1f38a9..7b0c5d98aa 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetUserDefinedVar.java
@@ -17,44 +17,23 @@
 
 package org.apache.doris.analysis;
 
-import org.apache.doris.thrift.TVarType;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.AnalysisException;
 
-// Set statement type.
-public enum SetType {
-    DEFAULT("DEFAULT"),
-    GLOBAL("GLOBAL"),
-    SESSION("SESSION");
-
-    private String desc;
-
-    SetType(String desc) {
-        this.desc = desc;
-    }
-
-    public TVarType toThrift() {
-        switch (this) {
-            case GLOBAL:
-                return TVarType.GLOBAL;
-            default:
-                return TVarType.SESSION;
-        }
-    }
-
-    public static SetType fromThrift(TVarType tType) {
-        switch (tType) {
-            case GLOBAL:
-                return SetType.GLOBAL;
-            default:
-                return SetType.SESSION;
-        }
-    }
-
-    public String toSql() {
-        return desc;
+public class SetUserDefinedVar extends SetVar {
+    public SetUserDefinedVar(String variable, Expr value) {
+        super(SetType.USER, variable, value, SetVarType.SET_USER_DEFINED_VAR);
     }
 
     @Override
-    public String toString() {
-        return toSql();
+    public void analyze(Analyzer analyzer) throws AnalysisException  {
+        Expr expression = getValue();
+        if (expression instanceof NullLiteral) {
+            setResult(NullLiteral.create(ScalarType.NULL));
+        } else if (expression instanceof LiteralExpr) {
+            setResult((LiteralExpr) expression);
+        } else {
+            throw new AnalysisException("Unsupported to set the non-literal for user defined variables.");
+        }
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetVar.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetVar.java
index ccb505bbe4..8afa942dba 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SetVar.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SetVar.java
@@ -43,7 +43,8 @@ public class SetVar {
         SET_LDAP_PASS_VAR,
         SET_NAMES_VAR,
         SET_TRANSACTION,
-        SET_USER_PROPERTY_VAR
+        SET_USER_PROPERTY_VAR,
+        SET_USER_DEFINED_VAR,
     }
 
     private String variable;
@@ -75,14 +76,36 @@ public class SetVar {
         }
     }
 
+    public SetVar(SetType setType, String variable, Expr value, SetVarType varType) {
+        this.type = setType;
+        this.varType = varType;
+        this.variable = variable;
+        this.value = value;
+        if (value instanceof LiteralExpr) {
+            this.result = (LiteralExpr) value;
+        }
+    }
+
     public String getVariable() {
         return variable;
     }
 
-    public LiteralExpr getValue() {
+    public Expr getValue() {
+        return value;
+    }
+
+    public void setValue(Expr value) {
+        this.value = value;
+    }
+
+    public LiteralExpr getResult() {
         return result;
     }
 
+    public void setResult(LiteralExpr result) {
+        this.result = result;
+    }
+
     public SetType getType() {
         return type;
     }
@@ -144,7 +167,7 @@ public class SetVar {
         }
 
         if (getVariable().equalsIgnoreCase(SessionVariable.PREFER_JOIN_METHOD)) {
-            String value = getValue().getStringValue();
+            String value = getResult().getStringValue();
             if (!value.equalsIgnoreCase("broadcast") && !value.equalsIgnoreCase("shuffle")) {
                 ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_VALUE_FOR_VAR,
                         SessionVariable.PREFER_JOIN_METHOD, value);
@@ -153,11 +176,11 @@ public class SetVar {
 
         // Check variable time_zone value is valid
         if (getVariable().equalsIgnoreCase(SessionVariable.TIME_ZONE)) {
-            this.value = new StringLiteral(TimeUtils.checkTimeZoneValidAndStandardize(getValue().getStringValue()));
+            this.value = new StringLiteral(TimeUtils.checkTimeZoneValidAndStandardize(getResult().getStringValue()));
             this.result = (LiteralExpr) this.value;
         }
         if (getVariable().equalsIgnoreCase(SessionVariable.PARALLEL_FRAGMENT_EXEC_INSTANCE_NUM)) {
-            int instanceNum = Integer.parseInt(getValue().getStringValue());
+            int instanceNum = Integer.parseInt(getResult().getStringValue());
             if (instanceNum > Config.max_instance_num) {
                 ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_VALUE_FOR_VAR,
                         SessionVariable.PARALLEL_FRAGMENT_EXEC_INSTANCE_NUM,
@@ -165,11 +188,11 @@ public class SetVar {
             }
         }
         if (getVariable().equalsIgnoreCase(SessionVariable.EXEC_MEM_LIMIT)) {
-            this.value = new StringLiteral(Long.toString(ParseUtil.analyzeDataVolumn(getValue().getStringValue())));
+            this.value = new StringLiteral(Long.toString(ParseUtil.analyzeDataVolumn(getResult().getStringValue())));
             this.result = (LiteralExpr) this.value;
         }
         if (getVariable().equalsIgnoreCase(SessionVariable.SCAN_QUEUE_MEM_LIMIT)) {
-            this.value = new StringLiteral(Long.toString(ParseUtil.analyzeDataVolumn(getValue().getStringValue())));
+            this.value = new StringLiteral(Long.toString(ParseUtil.analyzeDataVolumn(getResult().getStringValue())));
             this.result = (LiteralExpr) this.value;
         }
         if (getVariable().equalsIgnoreCase("is_report_success")) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SysVariableDesc.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/VariableExpr.java
similarity index 74%
rename from fe/fe-core/src/main/java/org/apache/doris/analysis/SysVariableDesc.java
rename to fe/fe-core/src/main/java/org/apache/doris/analysis/VariableExpr.java
index 023be528e6..649bb4017f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SysVariableDesc.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/VariableExpr.java
@@ -32,33 +32,37 @@ import org.apache.doris.thrift.TStringLiteral;
 
 import com.google.common.base.Strings;
 
+import java.math.BigDecimal;
 import java.util.Objects;
 
-// System variable
+// Variable expr: including the system variable and user define variable.
 // Converted to StringLiteral in analyze, if this variable is not exist, throw AnalysisException.
-public class SysVariableDesc extends Expr {
+public class VariableExpr extends Expr {
     private String name;
     private SetType setType;
+    private boolean isNull;
     private boolean boolValue;
     private long intValue;
     private double floatValue;
+    private BigDecimal decimalValue;
     private String strValue;
 
     private LiteralExpr literalExpr;
 
-    public SysVariableDesc(String name) {
+    public VariableExpr(String name) {
         this(name, SetType.SESSION);
     }
 
-    public SysVariableDesc(String name, SetType setType) {
+    public VariableExpr(String name, SetType setType) {
         this.name = name;
         this.setType = setType;
     }
 
-    protected SysVariableDesc(SysVariableDesc other) {
+    protected VariableExpr(VariableExpr other) {
         super(other);
         name = other.name;
         setType = other.setType;
+        isNull = other.isNull;
         boolValue = other.boolValue;
         intValue = other.intValue;
         floatValue = other.floatValue;
@@ -67,18 +71,22 @@ public class SysVariableDesc extends Expr {
 
     @Override
     public Expr clone() {
-        return new SysVariableDesc(this);
+        return new VariableExpr(this);
     }
 
     @Override
     public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
-        VariableMgr.fillValue(analyzer.getContext().getSessionVariable(), this);
-        if (!Strings.isNullOrEmpty(name) && VariableVarConverters.hasConverter(name)) {
-            setType(Type.VARCHAR);
-            try {
-                setStringValue(VariableVarConverters.decode(name, intValue));
-            } catch (DdlException e) {
-                ErrorReport.reportAnalysisException(e.getMessage());
+        if (setType == SetType.USER) {
+            VariableMgr.fillValueForUserDefinedVar(this);
+        } else {
+            VariableMgr.fillValue(analyzer.getContext().getSessionVariable(), this);
+            if (!Strings.isNullOrEmpty(name) && VariableVarConverters.hasConverter(name)) {
+                setType(Type.VARCHAR);
+                try {
+                    setStringValue(VariableVarConverters.decode(name, intValue));
+                } catch (DdlException e) {
+                    ErrorReport.reportAnalysisException(e.getMessage());
+                }
             }
         }
     }
@@ -91,6 +99,14 @@ public class SysVariableDesc extends Expr {
         return setType;
     }
 
+    public void setIsNull() {
+        isNull = true;
+    }
+
+    public boolean isNull() {
+        return isNull;
+    }
+
     public void setBoolValue(boolean value) {
         this.boolValue = value;
         this.literalExpr = new BoolLiteral(value);
@@ -106,6 +122,11 @@ public class SysVariableDesc extends Expr {
         this.literalExpr = new FloatLiteral(value);
     }
 
+    public void setDecimalValue(BigDecimal value) {
+        this.decimalValue = value;
+        this.literalExpr = new DecimalLiteral(value);
+    }
+
     public void setStringValue(String value) {
         this.strValue = value;
         this.literalExpr = new StringLiteral(value);
@@ -164,9 +185,14 @@ public class SysVariableDesc extends Expr {
 
     @Override
     public String toSqlImpl() {
-        StringBuilder sb = new StringBuilder("@@");
-        if (setType == SetType.GLOBAL) {
-            sb.append("GLOBAL.");
+        StringBuilder sb = new StringBuilder();
+        if (setType == SetType.USER) {
+            sb.append("@");
+        } else {
+            sb.append("@@");
+            if (setType == SetType.GLOBAL) {
+                sb.append("GLOBAL.");
+            }
         }
         sb.append(name);
         return sb.toString();
@@ -182,16 +208,16 @@ public class SysVariableDesc extends Expr {
         if (this == obj) {
             return true;
         }
-        if (!(obj instanceof SysVariableDesc)) {
+        if (!(obj instanceof VariableExpr)) {
             return false;
         }
-        if (!name.equals(((SysVariableDesc) obj).getName())) {
+        if (!name.equals(((VariableExpr) obj).getName())) {
             return false;
         }
-        if (!setType.equals(((SysVariableDesc) obj).getSetType())) {
+        if (!setType.equals(((VariableExpr) obj).getSetType())) {
             return false;
         }
 
-        return Objects.equals(literalExpr, ((SysVariableDesc) obj).getLiteralExpr());
+        return Objects.equals(literalExpr, ((VariableExpr) obj).getLiteralExpr());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 491b1e80f6..08f1ae00fb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -633,7 +633,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
     @Override
     public Expression visitUserVariable(UserVariableContext ctx) {
-        throw new ParseException("Unsupported user variable :" + ctx.getText(), ctx);
+        String name = ctx.identifierOrText().getText();
+        return VariableMgr.getLiteralForUserVar(name);
     }
 
     /**
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
index 109357499f..123aa618f4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.expressions.literal;
 
 import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.catalog.Type;
+import org.apache.doris.common.Config;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -75,6 +76,12 @@ public abstract class Literal extends Expression implements LeafExpression, Comp
             return new FloatLiteral((Float) value);
         } else if (value instanceof Double) {
             return new DoubleLiteral((Double) value);
+        } else if (value instanceof BigDecimal) {
+            if (Config.enable_decimal_conversion) {
+                return new DecimalV3Literal((BigDecimal) value);
+            } else {
+                return new DecimalLiteral((BigDecimal) value);
+            }
         } else if (value instanceof Boolean) {
             return BooleanLiteral.of((Boolean) value);
         } else if (value instanceof String) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SetExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SetExecutor.java
index c88e7e3336..9dbe9ca8a9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SetExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SetExecutor.java
@@ -22,6 +22,7 @@ import org.apache.doris.analysis.SetNamesVar;
 import org.apache.doris.analysis.SetPassVar;
 import org.apache.doris.analysis.SetStmt;
 import org.apache.doris.analysis.SetTransaction;
+import org.apache.doris.analysis.SetUserDefinedVar;
 import org.apache.doris.analysis.SetVar;
 import org.apache.doris.common.DdlException;
 
@@ -54,6 +55,8 @@ public class SetExecutor {
         } else if (var instanceof SetTransaction) {
             // do nothing
             return;
+        } else if (var instanceof SetUserDefinedVar) {
+            VariableMgr.setUserVar(var);
         } else {
             VariableMgr.setVar(ctx.getSessionVariable(), var);
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
index 5408e74071..24b7468e77 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/VariableMgr.java
@@ -17,9 +17,16 @@
 
 package org.apache.doris.qe;
 
+import org.apache.doris.analysis.BoolLiteral;
+import org.apache.doris.analysis.DecimalLiteral;
+import org.apache.doris.analysis.FloatLiteral;
+import org.apache.doris.analysis.IntLiteral;
+import org.apache.doris.analysis.LiteralExpr;
+import org.apache.doris.analysis.NullLiteral;
 import org.apache.doris.analysis.SetType;
 import org.apache.doris.analysis.SetVar;
-import org.apache.doris.analysis.SysVariableDesc;
+import org.apache.doris.analysis.StringLiteral;
+import org.apache.doris.analysis.VariableExpr;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
@@ -52,6 +59,7 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Field;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.locks.Lock;
@@ -116,6 +124,8 @@ public class VariableMgr {
     // its display name is "experimental_foo"
     private static ImmutableMap<String, VarContext> ctxByDisplayVarName;
 
+    private static Map<String, LiteralExpr> userVars = new HashMap<String, LiteralExpr>();
+
     // This variable is equivalent to the default value of session variables.
     // Whenever a new session is established, the value in this object is copied to the session-level variable.
     private static SessionVariable defaultSessionVariable;
@@ -258,6 +268,10 @@ public class VariableMgr {
         }
     }
 
+    public static void setUserVar(SetVar setVar) {
+        userVars.put(setVar.getVariable(), setVar.getResult());
+    }
+
     // Entry of handling SetVarStmt
     // Input:
     //      sessionVariable: the variable of current session
@@ -314,8 +328,8 @@ public class VariableMgr {
         VarAttr attr = ctx.getField().getAnnotation(VarAttr.class);
         String value;
         // If value is null, this is `set variable = DEFAULT`
-        if (setVar.getValue() != null) {
-            value = setVar.getValue().getStringValue();
+        if (setVar.getResult() != null) {
+            value = setVar.getResult().getStringValue();
         } else {
             value = ctx.getDefaultValue();
             if (value == null) {
@@ -416,7 +430,7 @@ public class VariableMgr {
     }
 
     // Get variable value through variable name, used to satisfy statement like `SELECT @@comment_version`
-    public static void fillValue(SessionVariable var, SysVariableDesc desc) throws AnalysisException {
+    public static void fillValue(SessionVariable var, VariableExpr desc) throws AnalysisException {
         VarContext ctx = ctxByVarName.get(desc.getName());
         if (ctx == null) {
             ErrorReport.reportAnalysisException(ErrorCode.ERR_UNKNOWN_SYSTEM_VARIABLE, desc.getName());
@@ -434,7 +448,7 @@ public class VariableMgr {
         }
     }
 
-    private static void fillValue(Object obj, Field field, SysVariableDesc desc) {
+    private static void fillValue(Object obj, Field field, VariableExpr desc) {
         try {
             switch (field.getType().getSimpleName()) {
                 case "boolean":
@@ -479,6 +493,36 @@ public class VariableMgr {
         }
     }
 
+    // Get variable value through variable name, used to satisfy statement like `SELECT @@comment_version`
+    public static void fillValueForUserDefinedVar(VariableExpr desc) {
+        String varName = desc.getName();
+        if (userVars.containsKey(varName)) {
+            LiteralExpr literalExpr = userVars.get(varName);
+            desc.setType(literalExpr.getType());
+            if (literalExpr instanceof BoolLiteral) {
+                desc.setBoolValue(((BoolLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof IntLiteral) {
+                desc.setIntValue(((IntLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof FloatLiteral) {
+                desc.setFloatValue(((FloatLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof DecimalLiteral) {
+                desc.setDecimalValue(((DecimalLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof StringLiteral) {
+                desc.setStringValue(((StringLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof NullLiteral) {
+                desc.setType(Type.NULL);
+                desc.setIsNull();
+            } else {
+                desc.setType(Type.VARCHAR);
+                desc.setStringValue("");
+            }
+        } else {
+            // If there are no such user defined var, just fill the NULL value.
+            desc.setType(Type.NULL);
+            desc.setIsNull();
+        }
+    }
+
     private static String getValue(SessionVariable var, String name, SetType setType) throws AnalysisException {
         VarContext ctx = ctxByVarName.get(name);
         if (ctx == null) {
@@ -499,7 +543,7 @@ public class VariableMgr {
 
     // Get variable value through variable name, used to satisfy statement like `SELECT @@comment_version`
     // For test only
-    public static String getValue(SessionVariable var, SysVariableDesc desc) throws AnalysisException {
+    public static String getValue(SessionVariable var, VariableExpr desc) throws AnalysisException {
         return getValue(var, desc.getName(), desc.getSetType());
     }
 
@@ -550,6 +594,30 @@ public class VariableMgr {
         return Literal.of("");
     }
 
+    public static @Nullable Literal getLiteralForUserVar(String varName) {
+        if (userVars.containsKey(varName)) {
+            LiteralExpr literalExpr = userVars.get(varName);
+            if (literalExpr instanceof BoolLiteral) {
+                return Literal.of(((BoolLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof IntLiteral) {
+                return Literal.of(((IntLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof FloatLiteral) {
+                return Literal.of(((FloatLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof DecimalLiteral) {
+                return Literal.of(((DecimalLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof StringLiteral) {
+                return Literal.of(((StringLiteral) literalExpr).getValue());
+            } else if (literalExpr instanceof NullLiteral) {
+                return Literal.of(null);
+            } else {
+                return Literal.of("");
+            }
+        } else {
+            // If there are no such user defined var, just return the NULL value.
+            return Literal.of(null);
+        }
+    }
+
     private static String getValue(Object obj, Field field) {
         try {
             switch (field.getType().getSimpleName()) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FoldConstantsRule.java b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FoldConstantsRule.java
index a4baf0ca3e..dcc0bede70 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/rewrite/FoldConstantsRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/rewrite/FoldConstantsRule.java
@@ -29,7 +29,7 @@ import org.apache.doris.analysis.Expr;
 import org.apache.doris.analysis.InformationFunction;
 import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.analysis.NullLiteral;
-import org.apache.doris.analysis.SysVariableDesc;
+import org.apache.doris.analysis.VariableExpr;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.ScalarType;
@@ -216,7 +216,7 @@ public class FoldConstantsRule implements ExprRewriteRule {
                 return;
             }
             // collect sysVariableDesc expr
-            if (expr.contains(Predicates.instanceOf(SysVariableDesc.class))) {
+            if (expr.contains(Predicates.instanceOf(VariableExpr.class))) {
                 getSysVarDescExpr(expr, sysVarMap);
                 return;
             }
@@ -244,14 +244,14 @@ public class FoldConstantsRule implements ExprRewriteRule {
     }
 
     private void getSysVarDescExpr(Expr expr, Map<String, Expr> sysVarMap) {
-        if (expr instanceof SysVariableDesc) {
-            Expr literalExpr = ((SysVariableDesc) expr).getLiteralExpr();
+        if (expr instanceof VariableExpr) {
+            Expr literalExpr = ((VariableExpr) expr).getLiteralExpr();
             if (literalExpr == null) {
                 try {
-                    VariableMgr.fillValue(ConnectContext.get().getSessionVariable(), (SysVariableDesc) expr);
-                    literalExpr = ((SysVariableDesc) expr).getLiteralExpr();
+                    VariableMgr.fillValue(ConnectContext.get().getSessionVariable(), (VariableExpr) expr);
+                    literalExpr = ((VariableExpr) expr).getLiteralExpr();
                 } catch (AnalysisException e) {
-                    LOG.warn("failed to get session variable value: " + ((SysVariableDesc) expr).getName());
+                    LOG.warn("failed to get session variable value: " + ((VariableExpr) expr).getName());
                 }
             }
             sysVarMap.put(expr.getId().toString(), literalExpr);
diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVarTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVarTest.java
index 7b5360c87c..d88538a9a5 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVarTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/SetVarTest.java
@@ -52,7 +52,7 @@ public class SetVarTest {
         var.setType(SetType.GLOBAL);
         Assert.assertEquals(SetType.GLOBAL, var.getType());
         Assert.assertEquals("names", var.getVariable());
-        Assert.assertEquals("utf-8", var.getValue().getStringValue());
+        Assert.assertEquals("utf-8", var.getResult().getStringValue());
 
         Assert.assertEquals("GLOBAL names = 'utf-8'", var.toString());
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/SysVariableDescTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/VariableExprTest.java
similarity index 92%
rename from fe/fe-core/src/test/java/org/apache/doris/analysis/SysVariableDescTest.java
rename to fe/fe-core/src/test/java/org/apache/doris/analysis/VariableExprTest.java
index fcc8b8ddf2..28334f4102 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/SysVariableDescTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/VariableExprTest.java
@@ -26,11 +26,11 @@ import org.apache.doris.thrift.TExprNodeType;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class SysVariableDescTest {
+public class VariableExprTest {
 
     @Test
     public void testNormal() throws AnalysisException {
-        SysVariableDesc desc = new SysVariableDesc("version_comment");
+        VariableExpr desc = new VariableExpr("version_comment");
         desc.analyze(AccessTestUtil.fetchAdminAnalyzer(false));
         Assert.assertEquals("@@version_comment", desc.toSql());
         Assert.assertEquals("version_comment", desc.getName());
@@ -45,7 +45,7 @@ public class SysVariableDescTest {
 
     @Test(expected = AnalysisException.class)
     public void testNoVar() throws AnalysisException {
-        SysVariableDesc desc = new SysVariableDesc("zcPrivate");
+        VariableExpr desc = new VariableExpr("zcPrivate");
         desc.analyze(AccessTestUtil.fetchAdminAnalyzer(false));
         Assert.fail("No exception throws.");
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
index b8abd92d32..f1b8e7b5a7 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/VariableMgrTest.java
@@ -22,7 +22,7 @@ import org.apache.doris.analysis.SetStmt;
 import org.apache.doris.analysis.SetType;
 import org.apache.doris.analysis.SetVar;
 import org.apache.doris.analysis.StringLiteral;
-import org.apache.doris.analysis.SysVariableDesc;
+import org.apache.doris.analysis.VariableExpr;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.DdlException;
@@ -154,7 +154,7 @@ public class VariableMgrTest {
         Assert.assertEquals(8L, ctx.getSessionVariable().getRuntimeFilterType());
 
         // Get from name
-        SysVariableDesc desc = new SysVariableDesc("exec_mem_limit");
+        VariableExpr desc = new VariableExpr("exec_mem_limit");
         Assert.assertEquals(var.getMaxExecMemByte() + "", VariableMgr.getValue(var, desc));
     }
 
diff --git a/regression-test/data/nereids_p0/test_user_var.out b/regression-test/data/nereids_p0/test_user_var.out
new file mode 100644
index 0000000000..41aebfe022
--- /dev/null
+++ b/regression-test/data/nereids_p0/test_user_var.out
@@ -0,0 +1,16 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !select1 --
+1	0	-1
+
+-- !select2 --
+1.1	0.0	-1.1
+
+-- !select3 --
+H	
+
+-- !select4 --
+true	false
+
+-- !select5 --
+\N	\N
+
diff --git a/regression-test/data/query_p0/set/test_user_var.out b/regression-test/data/query_p0/set/test_user_var.out
new file mode 100644
index 0000000000..41aebfe022
--- /dev/null
+++ b/regression-test/data/query_p0/set/test_user_var.out
@@ -0,0 +1,16 @@
+-- This file is automatically generated. You should know what you did if you want to edit this
+-- !select1 --
+1	0	-1
+
+-- !select2 --
+1.1	0.0	-1.1
+
+-- !select3 --
+H	
+
+-- !select4 --
+true	false
+
+-- !select5 --
+\N	\N
+
diff --git a/regression-test/suites/nereids_p0/test_user_var.groovy b/regression-test/suites/nereids_p0/test_user_var.groovy
new file mode 100644
index 0000000000..f54e139632
--- /dev/null
+++ b/regression-test/suites/nereids_p0/test_user_var.groovy
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_user_var") {
+    sql "SET enable_nereids_planner=true"
+    sql "SET enable_fallback_to_original_planner=false"
+    sql "SET @a1=1, @a2=0, @a3=-1"
+    sql "SET @b1=1.1, @b2=0.0, @b3=-1.1"
+    sql "SET @c1='H', @c2=''"
+    sql "SET @d1=true, @d2=false"
+    sql "SET @f1=null"
+
+    qt_select1 'select @a1, @a2, @a3;'
+    qt_select2 'select @b1, @b2, @b3;'
+    qt_select3 'select @c1, @c2;'
+    qt_select4 'select @d1, @d2;'
+    qt_select5 'select @f1, @f2;'
+}
\ No newline at end of file
diff --git a/regression-test/suites/query_p0/set/test_user_var.groovy b/regression-test/suites/query_p0/set/test_user_var.groovy
new file mode 100644
index 0000000000..af2d2caadc
--- /dev/null
+++ b/regression-test/suites/query_p0/set/test_user_var.groovy
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_user_var") {
+    sql "SET @a1=1, @a2=0, @a3=-1"
+    sql "SET @b1=1.1, @b2=0.0, @b3=-1.1"
+    sql "SET @c1='H', @c2=''"
+    sql "SET @d1=true, @d2=false"
+    sql "SET @f1=null"
+
+    qt_select1 'select @a1, @a2, @a3;'
+    qt_select2 'select @b1, @b2, @b3;'
+    qt_select3 'select @c1, @c2;'
+    qt_select4 'select @d1, @d2;'
+    qt_select5 'select @f1, @f2;'
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org