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 f314d968a3f [refactor](Expr) Extract getString* methods to 
ExprToStringValueVisitor (#61304)
f314d968a3f is described below

commit f314d968a3f06bdd49897794e622103cd019ba4e
Author: morrySnow <[email protected]>
AuthorDate: Tue Mar 17 19:36:46 2026 +0800

    [refactor](Expr) Extract getString* methods to ExprToStringValueVisitor 
(#61304)
    
    Move getStringValueForQuery, getStringValueInComplexTypeForQuery, and
    getStringValueForStreamLoad logic from 17 Expr subclasses into a
    centralized ExprToStringValueVisitor, following the same visitor pattern
    as ExprToSqlVisitor and ExprToThriftVisitor.
    
    New classes:
    - ExprToStringValueVisitor: visitor with visit methods for DateLiteral,
    FloatLiteral, BoolLiteral, NullLiteral, DecimalLiteral, ArrayLiteral,
    MapLiteral, StructLiteral, CastExpr, StringLiteral, IPv4Literal,
    IPv6Literal, VarBinaryLiteral, PlaceHolderExpr, JsonLiteral, MaxLiteral
    - StringValueContext: carries FormatOptions and mode flags
    (forStreamLoad, inComplexType)
    
    Expr base class methods now delegate to the visitor. Subclass overrides
    are removed. Also fixes pre-existing local variable naming in
    DateLiteral static block (DATE_KEY_FORMATTER -> dateKeyFmt) and makes
    convertToString private (no external callers).
    
    Co-authored-by: Copilot <[email protected]>
---
 .../org/apache/doris/analysis/ArrayLiteral.java    |  12 -
 .../org/apache/doris/analysis/BoolLiteral.java     |  10 -
 .../java/org/apache/doris/analysis/CastExpr.java   |  10 -
 .../org/apache/doris/analysis/DateLiteral.java     |  58 +--
 .../org/apache/doris/analysis/DecimalLiteral.java  |   6 -
 .../main/java/org/apache/doris/analysis/Expr.java  |  30 --
 .../doris/analysis/ExprToStringValueVisitor.java   | 254 ++++++++++
 .../org/apache/doris/analysis/FloatLiteral.java    |  33 --
 .../org/apache/doris/analysis/IPv4Literal.java     |   5 -
 .../org/apache/doris/analysis/IPv6Literal.java     |   5 -
 .../org/apache/doris/analysis/JsonLiteral.java     |   6 -
 .../org/apache/doris/analysis/LiteralExpr.java     |   5 -
 .../java/org/apache/doris/analysis/MapLiteral.java |  18 -
 .../java/org/apache/doris/analysis/MaxLiteral.java |   6 -
 .../org/apache/doris/analysis/NullLiteral.java     |  19 -
 .../org/apache/doris/analysis/PlaceHolderExpr.java |   5 -
 .../org/apache/doris/analysis/StringLiteral.java   |   6 -
 .../apache/doris/analysis/StringValueContext.java  |  70 +++
 .../org/apache/doris/analysis/StructLiteral.java   |  29 --
 .../apache/doris/analysis/VarBinaryLiteral.java    |   5 -
 .../trees/plans/commands/insert/InsertUtils.java   |   5 +-
 .../plans/physical/PhysicalOneRowRelation.java     |   9 +-
 .../java/org/apache/doris/qe/StmtExecutor.java     |   5 +-
 .../org/apache/doris/analysis/DateLiteralTest.java |   5 +-
 .../analysis/ExprToStringValueVisitorTest.java     | 536 +++++++++++++++++++++
 .../apache/doris/analysis/FloatLiteralTest.java    | 141 +++---
 .../analysis/VarBinaryLiteralAnalysisTest.java     |   4 +-
 .../trees/expressions/VarBinaryLiteralTest.java    |  10 +-
 28 files changed, 975 insertions(+), 332 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java
index 00a6d83d6be..e625e897414 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java
@@ -20,7 +20,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.ArrayType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import org.apache.commons.lang3.StringUtils;
 
@@ -75,17 +74,6 @@ public class ArrayLiteral extends LiteralExpr {
         return "[" + StringUtils.join(list, ", ") + "]";
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        List<String> list = new ArrayList<>(children.size());
-        ++options.level;
-        children.forEach(v -> {
-            list.add(v.getStringValueInComplexTypeForQuery(options));
-        });
-        --options.level;
-        return "[" + StringUtils.join(list, options.getCollectionDelim()) + 
"]";
-    }
-
     @Override
     public int hashCode() {
         return Objects.hashCode(children);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/BoolLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/BoolLiteral.java
index 501b3289f95..15fb1e0a1f0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/BoolLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/BoolLiteral.java
@@ -23,7 +23,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -108,15 +107,6 @@ public class BoolLiteral extends LiteralExpr {
         return value ? "1" : "0";
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        if (options.level > 0) {
-            return options.isBoolValueNum() ? getStringValue() : (value ? 
"true" : "false");
-        } else {
-            return getStringValue();
-        }
-    }
-
     @Override
     public ByteBuffer getHashValue(PrimitiveType type) {
         byte v = (byte) (value ? 1 : 0);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CastExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/CastExpr.java
index c12cec7811a..1aad243b1f2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CastExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CastExpr.java
@@ -21,7 +21,6 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -108,13 +107,4 @@ public class CastExpr extends Expr {
         return false;
     }
 
-    @Override
-    public String getStringValueForStreamLoad(FormatOptions options) {
-        return children.get(0).getStringValueForStreamLoad(options);
-    }
-
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return children.get(0).getStringValueInComplexTypeForQuery(options);
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
index 6d1f0224d6e..bc012f8a0ec 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
@@ -25,7 +25,6 @@ import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.InvalidFormatException;
-import org.apache.doris.foundation.format.FormatOptions;
 import org.apache.doris.nereids.util.DateUtils;
 
 import com.google.common.base.Preconditions;
@@ -90,29 +89,27 @@ public class DateLiteral extends LiteralExpr {
     private static final int MAX_MICROSECOND = 999999;
 
     private static List<DateTimeFormatter> formatterList = null;
-    /*
-     *  The datekey type is widely used in data warehouses
-     *  For example, 20121229 means '2012-12-29'
-     *  and data in the form of 'yyyymmdd' is generally called the datekey 
type.
-     */
-    private static DateTimeFormatter DATEKEY_FORMATTER = null;
-    // 'yyyymmddHHMMss'
-    private static DateTimeFormatter DATETIMEKEY_FORMATTER = null;
-
-    private static Map<String, Integer> MONTH_NAME_DICT = Maps.newHashMap();
-    private static Map<String, Integer> MONTH_ABBR_NAME_DICT = 
Maps.newHashMap();
-    private static Map<String, Integer> WEEK_DAY_NAME_DICT = Maps.newHashMap();
+
+    private static final Map<String, Integer> MONTH_NAME_DICT = 
Maps.newHashMap();
+    private static final Map<String, Integer> MONTH_ABBR_NAME_DICT = 
Maps.newHashMap();
+    private static final Map<String, Integer> WEEK_DAY_NAME_DICT = 
Maps.newHashMap();
     private static Set<Character> TIME_PART_SET = Sets.newHashSet();
-    private static String MICRO_SECOND_FORMATTER = "%f";
+    private static final String MICRO_SECOND_FORMATTER = "%f";
     private static final int[] DAYS_IN_MONTH = new int[]{0, 31, 28, 31, 30, 
31, 30, 31, 31, 30, 31, 30, 31};
     private static final WeekFields weekFields = 
WeekFields.of(DayOfWeek.SUNDAY, 7);
 
     static {
         try {
-            DATEKEY_FORMATTER = formatBuilder("%Y%m%d").toFormatter()
-                .withResolverStyle(ResolverStyle.STRICT);
-            DATETIMEKEY_FORMATTER = formatBuilder("%Y%m%d%H%i%s").toFormatter()
-                .withResolverStyle(ResolverStyle.STRICT);
+            /*
+             *  The datekey type is widely used in data warehouses
+             *  For example, 20121229 means '2012-12-29'
+             *  and data in the form of 'yyyymmdd' is generally called the 
datekey type.
+             */
+            DateTimeFormatter dateKeyFmt = 
formatBuilder("%Y%m%d").toFormatter()
+                    .withResolverStyle(ResolverStyle.STRICT);
+            // 'yyyymmddHHMMss'
+            DateTimeFormatter datetimeKeyFmt = 
formatBuilder("%Y%m%d%H%i%s").toFormatter()
+                    .withResolverStyle(ResolverStyle.STRICT);
             formatterList = Lists.newArrayList(
                 
formatBuilder("%Y%m%d").appendLiteral('T').appendPattern("HHmmss")
                     .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, true)
@@ -126,7 +123,7 @@ public class DateLiteral extends LiteralExpr {
                 formatBuilder("%Y%m%d%H%i%s")
                     .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 6, false)
                     .toFormatter().withResolverStyle(ResolverStyle.STRICT),
-                DATETIMEKEY_FORMATTER, DATEKEY_FORMATTER);
+                    datetimeKeyFmt, dateKeyFmt);
             TIME_PART_SET = "HhIiklrSsTp".chars().mapToObj(c -> (char) 
c).collect(Collectors.toSet());
         } catch (AnalysisException e) {
             LOG.error("invalid date format", e);
@@ -633,22 +630,6 @@ public class DateLiteral extends LiteralExpr {
         return this.type.isDate() || this.type.isDateV2();
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        if (!type.isTimeStampTz()) {
-            return getStringValue();
-        }
-        try {
-            String offset = 
DateUtils.getTimeZone().getRules().getOffset(java.time.Instant.now()).toString();
-            DateLiteral dateLiteral = new DateLiteral(getStringValue(),
-                    ScalarType.createDatetimeV2Type(((ScalarType) 
type).getScalarScale()));
-            return dateLiteral.getStringValue() + offset;
-        } catch (Exception e) {
-            LOG.warn("generate timestamptz({})'s string value for query 
failed. ", getStringValue(), e);
-            return getStringValue();
-        }
-    }
-
     @Override
     public String getStringValue() {
         char[] dateTimeChars = new char[26]; // Enough to hold "YYYY-MM-DD 
HH:MM:SS.mmmmmm"
@@ -728,17 +709,12 @@ public class DateLiteral extends LiteralExpr {
         return new String(dateTimeChars, 0, 19);
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
-
     public void roundFloor(int newScale) {
         microsecond = Double.valueOf(microsecond / (int) (Math.pow(10, 6 - 
newScale))
             * (Math.pow(10, 6 - newScale))).longValue();
     }
 
-    public String convertToString(PrimitiveType type) {
+    private String convertToString(PrimitiveType type) {
         if (type == PrimitiveType.DATE || type == PrimitiveType.DATEV2) {
             return String.format("%04d-%02d-%02d", year, month, day);
         } else if (type == PrimitiveType.DATETIMEV2 || type == 
PrimitiveType.TIMESTAMPTZ) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DecimalLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DecimalLiteral.java
index e86cf01577d..b723d0be8b3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DecimalLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DecimalLiteral.java
@@ -22,7 +22,6 @@ import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
-import org.apache.doris.foundation.format.FormatOptions;
 import org.apache.doris.qe.SessionVariable;
 
 import com.google.common.base.Preconditions;
@@ -239,11 +238,6 @@ public class DecimalLiteral extends NumericLiteralExpr {
         }
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        return value.toPlainString();
-    }
-
     @Override
     public <R, C> R accept(ExprVisitor<R, C> visitor, C context) {
         return visitor.visitDecimalLiteral(this, context);
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 6015724a60f..46cff0a9657 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
@@ -26,7 +26,6 @@ import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.NameFormatUtils;
 import org.apache.doris.common.TreeNode;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.base.Joiner;
 import com.google.common.base.MoreObjects;
@@ -392,35 +391,6 @@ public abstract class Expr extends TreeNode<Expr> 
implements Cloneable {
         return "";
     }
 
-    /**
-     * This method is used for constant fold of query in FE,
-     * for different serde dialect(hive, presto, doris).
-     */
-    public String getStringValueForQuery(FormatOptions options) {
-        return getStringValue();
-    }
-
-    /**
-     * This method is to return the string value of this expr in a complex 
type for query
-     * It is only used for "getStringValueForQuery()"
-     * For most of the integer types, it is same as getStringValueForQuery().
-     * But for others like StringLiteral and DateLiteral, it should be wrapped 
with quotations.
-     * eg: 1,2,abc,[1,2,3],["abc","def"],{10:20},{"abc":20}
-     */
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return getStringValueForQuery(options);
-    }
-
-    /**
-     * This method is to return the string value of this expr for stream load.
-     * so there is a little different from "getStringValueForQuery()".
-     * eg, for NullLiteral, it should be "\N" for stream load, but "null" for 
FE constant
-     * for StructLiteral, the value should not contain sub column's name.
-     */
-    public String getStringValueForStreamLoad(FormatOptions options) {
-        return getStringValueForQuery(options);
-    }
-
     /**
      * For excute expr the result is nullable
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ExprToStringValueVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ExprToStringValueVisitor.java
new file mode 100644
index 00000000000..13ca35f5510
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ExprToStringValueVisitor.java
@@ -0,0 +1,254 @@
+// 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.
+
+package org.apache.doris.analysis;
+
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.StructType;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.FractionalFormat;
+import org.apache.doris.foundation.format.FormatOptions;
+import org.apache.doris.nereids.util.DateUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Visitor that generates string values for Expr instances, handling different
+ * output modes (query, stream load) and complex type nesting.
+ *
+ * <p>This visitor extracts the logic previously in {@code 
getStringValueForQuery},
+ * {@code getStringValueInComplexTypeForQuery}, and {@code 
getStringValueForStreamLoad}
+ * from Expr subclasses, following the same visitor pattern as {@link 
ExprToSqlVisitor}.
+ */
+public class ExprToStringValueVisitor extends ExprVisitor<String, 
StringValueContext> {
+    private static final Logger LOG = 
LogManager.getLogger(ExprToStringValueVisitor.class);
+
+    public static final ExprToStringValueVisitor INSTANCE = new 
ExprToStringValueVisitor();
+
+    @Override
+    public String visit(Expr expr, StringValueContext ctx) {
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitDateLiteral(DateLiteral expr, StringValueContext ctx) {
+        String value;
+        if (expr.getType().isTimeStampTz()) {
+            try {
+                ZoneId dorisZone = DateUtils.getTimeZone();
+                String offset = 
dorisZone.getRules().getOffset(java.time.Instant.now()).toString();
+                DateLiteral dateLiteral = new 
DateLiteral(expr.getStringValue(),
+                        ScalarType.createDatetimeV2Type(((ScalarType) 
expr.getType()).getScalarScale()));
+                value = dateLiteral.getStringValue() + offset;
+            } catch (Exception e) {
+                LOG.warn("generate timestamptz({})'s string value for query 
failed. ",
+                        expr.getStringValue(), e);
+                value = expr.getStringValue();
+            }
+        } else {
+            value = expr.getStringValue();
+        }
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(value, ctx);
+        }
+        return value;
+    }
+
+    @Override
+    public String visitFloatLiteral(FloatLiteral expr, StringValueContext ctx) 
{
+        String value;
+        if (expr.getType() == Type.TIMEV2) {
+            String timeStr = expr.getStringValue();
+            value = timeStr.substring(1, timeStr.length() - 1);
+        } else {
+            double dValue = expr.getValue();
+            if (expr.getType() == Type.FLOAT) {
+                Float fValue = (float) dValue;
+                if (fValue.equals(Float.POSITIVE_INFINITY)) {
+                    dValue = Double.POSITIVE_INFINITY;
+                }
+                if (fValue.equals(Float.NEGATIVE_INFINITY)) {
+                    dValue = Double.NEGATIVE_INFINITY;
+                }
+            }
+            value = FractionalFormat.getFormatStringValue(dValue,
+                    expr.getType() == Type.DOUBLE ? 16 : 7,
+                    expr.getType() == Type.DOUBLE ? "%.15E" : "%.6E");
+        }
+        if (ctx.isInComplexType() && expr.getType() == Type.TIMEV2) {
+            return wrapWithQuotes(value, ctx);
+        }
+        return value;
+    }
+
+    @Override
+    public String visitBoolLiteral(BoolLiteral expr, StringValueContext ctx) {
+        FormatOptions options = ctx.getFormatOptions();
+        if (options.level > 0) {
+            return options.isBoolValueNum() ? expr.getStringValue() : 
(expr.getValue() ? "true" : "false");
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitNullLiteral(NullLiteral expr, StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return ctx.getFormatOptions().getNullFormat();
+        }
+        if (ctx.isForStreamLoad()) {
+            return FeConstants.null_string;
+        }
+        return null;
+    }
+
+    @Override
+    public String visitDecimalLiteral(DecimalLiteral expr, StringValueContext 
ctx) {
+        return expr.getValue().toPlainString();
+    }
+
+    @Override
+    public String visitArrayLiteral(ArrayLiteral expr, StringValueContext ctx) 
{
+        FormatOptions options = ctx.getFormatOptions();
+        List<String> list = new ArrayList<>(expr.getChildren().size());
+        ++options.level;
+        for (Expr child : expr.getChildren()) {
+            list.add(child.accept(this, ctx.asComplexType()));
+        }
+        --options.level;
+        return "[" + StringUtils.join(list, options.getCollectionDelim()) + 
"]";
+    }
+
+    @Override
+    public String visitMapLiteral(MapLiteral expr, StringValueContext ctx) {
+        FormatOptions options = ctx.getFormatOptions();
+        List<Expr> children = expr.getChildren();
+        List<String> list = new ArrayList<>(children.size());
+        ++options.level;
+        StringValueContext childCtx = ctx.asComplexType();
+        for (int i = 0; i < children.size() && i + 1 < children.size(); i += 
2) {
+            if (children.get(i).getType().isComplexType()) {
+                throw new UnsupportedOperationException(
+                        "Unsupported key type for MAP: " + 
children.get(i).getType());
+            }
+            list.add(children.get(i).accept(this, childCtx)
+                    + options.getMapKeyDelim()
+                    + children.get(i + 1).accept(this, childCtx));
+        }
+        --options.level;
+        return "{" + StringUtils.join(list, options.getCollectionDelim()) + 
"}";
+    }
+
+    @Override
+    public String visitStructLiteral(StructLiteral expr, StringValueContext 
ctx) {
+        FormatOptions options = ctx.getFormatOptions();
+        List<Expr> children = expr.getChildren();
+        List<String> list = new ArrayList<>(children.size());
+        if (ctx.isForStreamLoad()) {
+            for (int i = 0; i < children.size(); i++) {
+                list.add(children.get(i).accept(this, 
ctx.asQueryComplexType()));
+            }
+        } else {
+            ++options.level;
+            StringValueContext childCtx = ctx.asComplexType();
+            for (int i = 0; i < children.size(); i++) {
+                list.add(options.getNestedStringWrapper()
+                        + ((StructType) 
expr.getType()).getFields().get(i).getName()
+                        + options.getNestedStringWrapper()
+                        + options.getMapKeyDelim()
+                        + children.get(i).accept(this, childCtx));
+            }
+            --options.level;
+        }
+        return "{" + StringUtils.join(list, options.getCollectionDelim()) + 
"}";
+    }
+
+    @Override
+    public String visitCastExpr(CastExpr expr, StringValueContext ctx) {
+        if (ctx.isInComplexType() || ctx.isForStreamLoad()) {
+            return expr.getChildren().get(0).accept(this, ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitStringLiteral(StringLiteral expr, StringValueContext 
ctx) {
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(expr.getStringValue(), ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitIPv4Literal(IPv4Literal expr, StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(expr.getStringValue(), ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitIPv6Literal(IPv6Literal expr, StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(expr.getStringValue(), ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitVarBinaryLiteral(VarBinaryLiteral expr, 
StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(expr.getStringValue(), ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitPlaceHolderExpr(PlaceHolderExpr expr, 
StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return wrapWithQuotes(expr.getStringValue(), ctx);
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitJsonLiteral(JsonLiteral expr, StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return null;
+        }
+        return expr.getStringValue();
+    }
+
+    @Override
+    public String visitMaxLiteral(MaxLiteral expr, StringValueContext ctx) {
+        if (ctx.isInComplexType()) {
+            return null;
+        }
+        return expr.getStringValue();
+    }
+
+    private String wrapWithQuotes(String value, StringValueContext ctx) {
+        String wrapper = ctx.getFormatOptions().getNestedStringWrapper();
+        return wrapper + value + wrapper;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/FloatLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/FloatLiteral.java
index 437bfc463a3..f511a8ddb10 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FloatLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FloatLiteral.java
@@ -20,8 +20,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.common.FractionalFormat;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -148,37 +146,6 @@ public class FloatLiteral extends NumericLiteralExpr {
         return nf.format(value);
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        if (type == Type.TIMEV2) {
-            // FloatLiteral used to represent TIME type, here we need to 
remove apostrophe from timeStr
-            // for example '11:22:33' -> 11:22:33
-            String timeStr = getStringValue();
-            return timeStr.substring(1, timeStr.length() - 1);
-        } else {
-            if (type == Type.FLOAT) {
-                Float fValue = (float) value;
-                if (fValue.equals(Float.POSITIVE_INFINITY)) {
-                    value = Double.POSITIVE_INFINITY;
-                }
-                if (fValue.equals(Float.NEGATIVE_INFINITY)) {
-                    value = Double.NEGATIVE_INFINITY;
-                }
-            }
-            return FractionalFormat.getFormatStringValue(value, type == 
Type.DOUBLE ? 16 : 7,
-                    type == Type.DOUBLE ? "%.15E" : "%.6E");
-        }
-    }
-
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        String ret = this.getStringValueForQuery(options);
-        if (type == Type.TIMEV2) {
-            ret = options.getNestedStringWrapper() + ret + 
options.getNestedStringWrapper();
-        }
-        return ret;
-    }
-
     @Override
     public long getLongValue() {
         return (long) value;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv4Literal.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv4Literal.java
index 9240ce66085..3348c982733 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv4Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv4Literal.java
@@ -19,7 +19,6 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -121,8 +120,4 @@ public class IPv4Literal extends LiteralExpr {
         return value;
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv6Literal.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv6Literal.java
index d1149608da6..7a46f74368a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv6Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/IPv6Literal.java
@@ -19,7 +19,6 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.annotations.SerializedName;
 
@@ -103,8 +102,4 @@ public class IPv6Literal extends LiteralExpr {
         return value;
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/JsonLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/JsonLiteral.java
index 199c8502657..01b64495b23 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/JsonLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/JsonLiteral.java
@@ -19,7 +19,6 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.gson.JsonParser;
 import com.google.gson.JsonSyntaxException;
@@ -89,11 +88,6 @@ public class JsonLiteral extends LiteralExpr {
         return value;
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return null;
-    }
-
     public String getUnescapedValue() {
         // Unescape string exactly like Hive does. Hive's method assumes
         // quotes so we add them here to reuse Hive's code.
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
index ba3a13b35b8..bb140cdcdfd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LiteralExpr.java
@@ -23,7 +23,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.base.Preconditions;
 
@@ -150,10 +149,6 @@ public abstract class LiteralExpr extends Expr implements 
Comparable<LiteralExpr
     @Override
     public abstract String getStringValue();
 
-    public String getStringValueForQuery(FormatOptions options) {
-        return getStringValue();
-    }
-
     public long getLongValue() {
         return 0;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java
index 59d751c6738..2ffa94a8df0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/MapLiteral.java
@@ -20,7 +20,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.MapType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.collect.Lists;
 import org.apache.commons.lang3.StringUtils;
@@ -79,23 +78,6 @@ public class MapLiteral extends LiteralExpr {
         return expr.getStringValue();
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        List<String> list = new ArrayList<>(children.size());
-        ++options.level;
-        for (int i = 0; i < children.size() && i + 1 < children.size(); i += 
2) {
-            // we should use type to decide we output array is suitable for 
json format
-            if (children.get(i).getType().isComplexType()) {
-                // map key type do not support complex type
-                throw new UnsupportedOperationException("Unsupported key type 
for MAP: " + children.get(i).getType());
-            }
-            
list.add(children.get(i).getStringValueInComplexTypeForQuery(options)
-                    + options.getMapKeyDelim() + children.get(i + 
1).getStringValueInComplexTypeForQuery(options));
-        }
-        --options.level;
-        return "{" + StringUtils.join(list, options.getCollectionDelim()) + 
"}";
-    }
-
     @Override
     public <R, C> R accept(ExprVisitor<R, C> visitor, C context) {
         return visitor.visitMapLiteral(this, context);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/MaxLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/MaxLiteral.java
index 3ea986dfa69..989003ddc09 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/MaxLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/MaxLiteral.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.analysis;
 
-import org.apache.doris.foundation.format.FormatOptions;
-
 public final class MaxLiteral extends LiteralExpr {
 
     public static final MaxLiteral MAX_VALUE = new MaxLiteral();
@@ -60,8 +58,4 @@ public final class MaxLiteral extends LiteralExpr {
         return null;
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return null;
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/NullLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/NullLiteral.java
index d5698141700..1a2737ba298 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/NullLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/NullLiteral.java
@@ -23,8 +23,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.common.FeConstants;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import java.nio.ByteBuffer;
 
@@ -94,23 +92,6 @@ public class NullLiteral extends LiteralExpr {
         return "NULL";
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        return null;
-    }
-
-    @Override
-    public String getStringValueForStreamLoad(FormatOptions options) {
-        return FeConstants.null_string;
-    }
-
-    // the null value inside an array is represented as "null", for exampe:
-    // [null, null]. Not same as other primitive type to represent as \N.
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNullFormat();
-    }
-
     @Override
     public long getLongValue() {
         return 0;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
index 53bc9af9239..fa276cd66b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/PlaceHolderExpr.java
@@ -21,7 +21,6 @@ import org.apache.doris.catalog.MysqlColType;
 import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.base.Preconditions;
 
@@ -119,9 +118,5 @@ public class PlaceHolderExpr extends LiteralExpr {
         return visitor.visitPlaceHolderExpr(this, context);
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
 
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
index c7fa50b6cf8..371d39aa6b6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringLiteral.java
@@ -21,7 +21,6 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.base.Preconditions;
 import com.google.gson.annotations.SerializedName;
@@ -121,11 +120,6 @@ public class StringLiteral extends LiteralExpr {
         return value;
     }
 
-    @Override
-    protected String getStringValueInComplexTypeForQuery(FormatOptions 
options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
-
     @Override
     public long getLongValue() {
         return Long.valueOf(value);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/StringValueContext.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringValueContext.java
new file mode 100644
index 00000000000..5c0830bb938
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StringValueContext.java
@@ -0,0 +1,70 @@
+// 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.
+
+package org.apache.doris.analysis;
+
+import org.apache.doris.foundation.format.FormatOptions;
+
+/**
+ * Context for {@link ExprToStringValueVisitor}, carrying format options and 
mode information.
+ */
+public class StringValueContext {
+    private final FormatOptions formatOptions;
+    private final boolean forStreamLoad;
+    private final boolean inComplexType;
+
+    private StringValueContext(FormatOptions formatOptions, boolean 
forStreamLoad, boolean inComplexType) {
+        this.formatOptions = formatOptions;
+        this.forStreamLoad = forStreamLoad;
+        this.inComplexType = inComplexType;
+    }
+
+    public static StringValueContext forQuery(FormatOptions options) {
+        return new StringValueContext(options, false, false);
+    }
+
+    public static StringValueContext forStreamLoad(FormatOptions options) {
+        return new StringValueContext(options, true, false);
+    }
+
+    public StringValueContext asComplexType() {
+        if (inComplexType) {
+            return this;
+        }
+        return new StringValueContext(formatOptions, forStreamLoad, true);
+    }
+
+    /**
+     * Returns a context in query + complex-type mode, regardless of the 
current mode.
+     * Used by StructLiteral's stream-load path where children are rendered in 
query-complex mode.
+     */
+    public StringValueContext asQueryComplexType() {
+        return new StringValueContext(formatOptions, false, true);
+    }
+
+    public FormatOptions getFormatOptions() {
+        return formatOptions;
+    }
+
+    public boolean isForStreamLoad() {
+        return forStreamLoad;
+    }
+
+    public boolean isInComplexType() {
+        return inComplexType;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java
index bdf09dfb6df..8476f446417 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/StructLiteral.java
@@ -20,7 +20,6 @@ package org.apache.doris.analysis;
 import org.apache.doris.catalog.StructType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import org.apache.commons.lang3.StringUtils;
 
@@ -77,34 +76,6 @@ public class StructLiteral extends LiteralExpr {
         return "{" + StringUtils.join(list, ", ") + "}";
     }
 
-    @Override
-    public String getStringValueForQuery(FormatOptions options) {
-        List<String> list = new ArrayList<>(children.size());
-        ++options.level;
-        // same with be default field index start with 1
-        for (int i = 0; i < children.size(); i++) {
-            Expr child = children.get(i);
-            list.add(options.getNestedStringWrapper()
-                    + ((StructType) type).getFields().get(i).getName()
-                    + options.getNestedStringWrapper()
-                    + options.getMapKeyDelim()
-                    + child.getStringValueInComplexTypeForQuery(options));
-        }
-        --options.level;
-        return "{" + StringUtils.join(list, options.getCollectionDelim()) + 
"}";
-    }
-
-    @Override
-    public String getStringValueForStreamLoad(FormatOptions options) {
-        List<String> list = new ArrayList<>(children.size());
-        // same with be default field index start with 1
-        for (int i = 0; i < children.size(); i++) {
-            Expr child = children.get(i);
-            list.add(child.getStringValueInComplexTypeForQuery(options));
-        }
-        return "{" + StringUtils.join(list, options.getCollectionDelim()) + 
"}";
-    }
-
     @Override
     public Expr clone() {
         return new StructLiteral(this);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/VarBinaryLiteral.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/VarBinaryLiteral.java
index dd0e890150c..b081e7be17f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/VarBinaryLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/VarBinaryLiteral.java
@@ -19,7 +19,6 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
-import org.apache.doris.foundation.format.FormatOptions;
 
 import com.google.common.io.BaseEncoding;
 import com.google.gson.annotations.SerializedName;
@@ -121,8 +120,4 @@ public class VarBinaryLiteral extends LiteralExpr {
         return new String(value, StandardCharsets.ISO_8859_1);
     }
 
-    @Override
-    public String getStringValueInComplexTypeForQuery(FormatOptions options) {
-        return options.getNestedStringWrapper() + 
getStringValueForQuery(options) + options.getNestedStringWrapper();
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
index 663c8e57d8b..12d0ddd859a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java
@@ -17,6 +17,8 @@
 
 package org.apache.doris.nereids.trees.plans.commands.insert;
 
+import org.apache.doris.analysis.ExprToStringValueVisitor;
+import org.apache.doris.analysis.StringValueContext;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
@@ -182,7 +184,8 @@ public class InsertUtils {
                 throw new AnalysisException(
                         "do not support non-literal expr in transactional 
insert operation: " + expr.toSql());
             }
-            row.addColBuilder().setValue(((Literal) 
expr).toLegacyLiteral().getStringValueForStreamLoad(options));
+            row.addColBuilder().setValue(((Literal) 
expr).toLegacyLiteral().accept(
+                    ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forStreamLoad(options)));
         }
         return row.build();
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
index ad811526a54..44a067829a1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
@@ -17,7 +17,9 @@
 
 package org.apache.doris.nereids.trees.plans.physical;
 
+import org.apache.doris.analysis.ExprToStringValueVisitor;
 import org.apache.doris.analysis.LiteralExpr;
+import org.apache.doris.analysis.StringValueContext;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.nereids.CascadesContext;
@@ -177,7 +179,7 @@ public class PhysicalOneRowRelation extends 
PhysicalRelation implements OneRowRe
                     columns.add(new Column(output.getName(), 
output.getDataType().toCatalogDataType()));
                     if 
(output.getDataType().toCatalogDataType().isVarbinaryType()) {
                         // The FE (computeResultInFe) can currently only build 
a ResultSet<List<List<String>>>.
-                        // If we materialize a VARBINARY literal via 
legacyExpr.getStringValueForQuery():
+                        // If we materialize a VARBINARY literal via 
ExprToStringValueVisitor:
                         //   1) We first wrap the raw bytes in a Java String.
                         //   2) Later StmtExecutor.sendTextResultRow 
re-encodes that String as UTF-8 when
                         //      writing the MySQL wire protocol. This may 
expand bytes (e.g. 0xAB becomes
@@ -188,8 +190,9 @@ public class PhysicalOneRowRelation extends 
PhysicalRelation implements OneRowRe
                         // so we can VARBINARY safely and remove this early 
return.
                         return Optional.empty();
                     }
-                    data.add(legacyExpr.getStringValueForQuery(
-                            
cascadesContext.getStatementContext().getFormatOptions()));
+                    
data.add(legacyExpr.accept(ExprToStringValueVisitor.INSTANCE,
+                            StringValueContext.forQuery(
+                                    
cascadesContext.getStatementContext().getFormatOptions())));
                 } else {
                     return Optional.empty();
                 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java 
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 3a42505ba50..e3cc5ea106c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -19,6 +19,7 @@ package org.apache.doris.qe;
 
 import org.apache.doris.analysis.Expr;
 import org.apache.doris.analysis.ExprToSqlVisitor;
+import org.apache.doris.analysis.ExprToStringValueVisitor;
 import org.apache.doris.analysis.OutFileClause;
 import org.apache.doris.analysis.PlaceHolderExpr;
 import org.apache.doris.analysis.Queriable;
@@ -26,6 +27,7 @@ import org.apache.doris.analysis.RedirectStatus;
 import org.apache.doris.analysis.StatementBase;
 import org.apache.doris.analysis.StorageBackend;
 import org.apache.doris.analysis.StorageBackend.StorageType;
+import org.apache.doris.analysis.StringValueContext;
 import org.apache.doris.analysis.ToSqlParams;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.DatabaseIf;
@@ -287,7 +289,8 @@ public class StmtExecutor {
                         "do not support non-literal expr in transactional 
insert operation: "
                                 + expr.accept(ExprToSqlVisitor.INSTANCE, 
ToSqlParams.WITH_TABLE));
             }
-            
row.addColBuilder().setValue(expr.getStringValueForStreamLoad(options));
+            
row.addColBuilder().setValue(expr.accept(ExprToStringValueVisitor.INSTANCE,
+                    StringValueContext.forStreamLoad(options)));
         }
         return row.build();
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java
index 7d797e21b5b..f1be4f4d9b5 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/DateLiteralTest.java
@@ -19,6 +19,7 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.common.AnalysisException;
+import org.apache.doris.foundation.format.FormatOptions;
 import org.apache.doris.qe.ConnectContext;
 
 import org.junit.jupiter.api.Assertions;
@@ -51,12 +52,12 @@ public class DateLiteralTest {
             timeZone = "+08:00";
             context.getSessionVariable().setTimeZone(timeZone);
             expected = "2020-02-02 20:00:03.123456+08:00";
-            Assertions.assertEquals(expected, 
dateLiteral.getStringValueForQuery(null));
+            Assertions.assertEquals(expected, 
dateLiteral.accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
 
             timeZone = "-08:00";
             context.getSessionVariable().setTimeZone(timeZone);
             expected = "2020-02-02 04:00:03.123456-08:00";
-            Assertions.assertEquals(expected, 
dateLiteral.getStringValueForQuery(null));
+            Assertions.assertEquals(expected, 
dateLiteral.accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
         } finally {
             ConnectContext.remove();
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/ExprToStringValueVisitorTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/ExprToStringValueVisitorTest.java
new file mode 100644
index 00000000000..439d9d97aa4
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/ExprToStringValueVisitorTest.java
@@ -0,0 +1,536 @@
+// 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.
+
+package org.apache.doris.analysis;
+
+import org.apache.doris.catalog.ArrayType;
+import org.apache.doris.catalog.MapType;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.StructField;
+import org.apache.doris.catalog.StructType;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.foundation.format.FormatOptions;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+public class ExprToStringValueVisitorTest {
+
+    private static final ExprToStringValueVisitor V = 
ExprToStringValueVisitor.INSTANCE;
+
+    // ======================== BoolLiteral ========================
+
+    @Test
+    public void testBoolLiteralQueryTopLevel() {
+        BoolLiteral t = new BoolLiteral(true);
+        BoolLiteral f = new BoolLiteral(false);
+        FormatOptions opts = FormatOptions.getDefault();
+        // level=0: uses getStringValue() which returns "1"/"0"
+        Assertions.assertEquals("1", V.visitBoolLiteral(t, 
StringValueContext.forQuery(opts)));
+        Assertions.assertEquals("0", V.visitBoolLiteral(f, 
StringValueContext.forQuery(opts)));
+    }
+
+    @Test
+    public void testBoolLiteralNestedDefaultFormat() {
+        BoolLiteral t = new BoolLiteral(true);
+        BoolLiteral f = new BoolLiteral(false);
+        FormatOptions opts = FormatOptions.getDefault();
+        // Default: isBoolValueNum=true, so nested returns "1"/"0"
+        opts.level = 1;
+        Assertions.assertEquals("1", V.visitBoolLiteral(t, 
StringValueContext.forQuery(opts)));
+        Assertions.assertEquals("0", V.visitBoolLiteral(f, 
StringValueContext.forQuery(opts)));
+    }
+
+    @Test
+    public void testBoolLiteralNestedHiveFormat() {
+        BoolLiteral t = new BoolLiteral(true);
+        BoolLiteral f = new BoolLiteral(false);
+        FormatOptions opts = FormatOptions.getForHive();
+        // Hive: isBoolValueNum=false, nested returns "true"/"false"
+        opts.level = 1;
+        Assertions.assertEquals("true", V.visitBoolLiteral(t, 
StringValueContext.forQuery(opts)));
+        Assertions.assertEquals("false", V.visitBoolLiteral(f, 
StringValueContext.forQuery(opts)));
+    }
+
+    // ======================== NullLiteral ========================
+
+    @Test
+    public void testNullLiteralQueryTopLevel() {
+        NullLiteral n = new NullLiteral();
+        Assertions.assertNull(V.visitNullLiteral(n, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testNullLiteralStreamLoad() {
+        NullLiteral n = new NullLiteral();
+        Assertions.assertEquals(FeConstants.null_string,
+                V.visitNullLiteral(n, 
StringValueContext.forStreamLoad(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testNullLiteralInComplexTypeDefault() {
+        NullLiteral n = new NullLiteral();
+        // Default nullFormat = "null"
+        Assertions.assertEquals("null",
+                V.visitNullLiteral(n, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    @Test
+    public void testNullLiteralInComplexTypePresto() {
+        NullLiteral n = new NullLiteral();
+        // Presto nullFormat = "NULL"
+        Assertions.assertEquals("NULL",
+                V.visitNullLiteral(n, 
StringValueContext.forQuery(FormatOptions.getForPresto()).asComplexType()));
+    }
+
+    @Test
+    public void testNullLiteralInComplexTypeStreamLoad() {
+        NullLiteral n = new NullLiteral();
+        // Inside a complex type during stream load, inComplexType takes 
precedence over forStreamLoad.
+        // Old behavior: ArrayLiteral.getStringValueForStreamLoad -> 
getStringValueForQuery
+        //   -> child.getStringValueInComplexTypeForQuery -> getNullFormat() = 
"null"
+        Assertions.assertEquals("null",
+                V.visitNullLiteral(n,
+                        
StringValueContext.forStreamLoad(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== DecimalLiteral ========================
+
+    @Test
+    public void testDecimalLiteralPlainString() throws Exception {
+        DecimalLiteral d = new DecimalLiteral("123.45");
+        Assertions.assertEquals("123.45", V.visitDecimalLiteral(d, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testDecimalLiteralNoScientificNotation() throws Exception {
+        DecimalLiteral d = new DecimalLiteral("100000000000000000.123");
+        // toPlainString avoids scientific notation
+        Assertions.assertFalse(V.visitDecimalLiteral(d,
+                
StringValueContext.forQuery(FormatOptions.getDefault())).contains("E"));
+    }
+
+    // ======================== StringLiteral ========================
+
+    @Test
+    public void testStringLiteralQueryTopLevel() {
+        StringLiteral s = new StringLiteral("hello");
+        Assertions.assertEquals("hello",
+                V.visitStringLiteral(s, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testStringLiteralInComplexType() {
+        StringLiteral s = new StringLiteral("hello");
+        // Default wrapper = "
+        Assertions.assertEquals("\"hello\"",
+                V.visitStringLiteral(s, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    @Test
+    public void testStringLiteralInComplexTypePresto() {
+        StringLiteral s = new StringLiteral("hello");
+        // Presto wrapper = "" (empty)
+        Assertions.assertEquals("hello",
+                V.visitStringLiteral(s, 
StringValueContext.forQuery(FormatOptions.getForPresto()).asComplexType()));
+    }
+
+    // ======================== IPv4Literal ========================
+
+    @Test
+    public void testIPv4LiteralQuery() throws Exception {
+        IPv4Literal ip = new IPv4Literal("192.168.1.1");
+        Assertions.assertEquals("192.168.1.1",
+                V.visitIPv4Literal(ip, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testIPv4LiteralInComplexType() throws Exception {
+        IPv4Literal ip = new IPv4Literal("10.0.0.1");
+        Assertions.assertEquals("\"10.0.0.1\"",
+                V.visitIPv4Literal(ip, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== IPv6Literal ========================
+
+    @Test
+    public void testIPv6LiteralQuery() throws Exception {
+        IPv6Literal ip = new IPv6Literal("::1");
+        Assertions.assertEquals("::1",
+                V.visitIPv6Literal(ip, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testIPv6LiteralInComplexType() throws Exception {
+        IPv6Literal ip = new IPv6Literal("fe80::1");
+        Assertions.assertEquals("\"fe80::1\"",
+                V.visitIPv6Literal(ip, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== VarBinaryLiteral ========================
+
+    @Test
+    public void testVarBinaryLiteralQuery() throws Exception {
+        VarBinaryLiteral vb = new VarBinaryLiteral("hello".getBytes());
+        Assertions.assertEquals("hello",
+                V.visitVarBinaryLiteral(vb, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testVarBinaryLiteralInComplexType() throws Exception {
+        VarBinaryLiteral vb = new VarBinaryLiteral("test".getBytes());
+        Assertions.assertEquals("\"test\"",
+                V.visitVarBinaryLiteral(vb, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== PlaceHolderExpr ========================
+
+    @Test
+    public void testPlaceHolderExprInComplexType() {
+        PlaceHolderExpr ph = new PlaceHolderExpr();
+        // PlaceHolderExpr.getStringValue() returns ""
+        Assertions.assertEquals("\"\"",
+                V.visitPlaceHolderExpr(ph, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== JsonLiteral ========================
+
+    @Test
+    public void testJsonLiteralQuery() throws Exception {
+        JsonLiteral j = new JsonLiteral("{\"a\":1}");
+        Assertions.assertEquals("{\"a\":1}",
+                V.visitJsonLiteral(j, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testJsonLiteralInComplexTypeReturnsNull() throws Exception {
+        JsonLiteral j = new JsonLiteral("{\"a\":1}");
+        Assertions.assertNull(
+                V.visitJsonLiteral(j, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== MaxLiteral ========================
+
+    @Test
+    public void testMaxLiteralQuery() {
+        Assertions.assertNull(
+                V.visitMaxLiteral(MaxLiteral.MAX_VALUE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testMaxLiteralInComplexTypeReturnsNull() {
+        Assertions.assertNull(
+                V.visitMaxLiteral(MaxLiteral.MAX_VALUE,
+                        
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== FloatLiteral ========================
+
+    @Test
+    public void testFloatLiteralDouble() {
+        FloatLiteral f = new FloatLiteral(3.14, Type.DOUBLE);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        // FractionalFormat should produce a numeric string
+        Assertions.assertNotNull(result);
+        Assertions.assertTrue(result.contains("3.14"));
+    }
+
+    @Test
+    public void testFloatLiteralFloat() {
+        FloatLiteral f = new FloatLiteral(2.5, Type.FLOAT);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertNotNull(result);
+        Assertions.assertTrue(result.contains("2.5"));
+    }
+
+    @Test
+    public void testFloatLiteralPositiveInfinity() {
+        FloatLiteral f = new FloatLiteral(Double.POSITIVE_INFINITY, 
Type.DOUBLE);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("Infinity", result);
+    }
+
+    @Test
+    public void testFloatLiteralNegativeInfinity() {
+        FloatLiteral f = new FloatLiteral(Double.NEGATIVE_INFINITY, 
Type.DOUBLE);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("-Infinity", result);
+    }
+
+    @Test
+    public void testFloatLiteralNaN() {
+        FloatLiteral f = new FloatLiteral(Double.NaN, Type.DOUBLE);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("NaN", result);
+    }
+
+    @Test
+    public void testFloatLiteralFloatTypePositiveInfinity() {
+        // FLOAT infinity gets promoted to DOUBLE infinity
+        FloatLiteral f = new FloatLiteral((double) Float.POSITIVE_INFINITY, 
Type.FLOAT);
+        String result = V.visitFloatLiteral(f, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("Infinity", result);
+    }
+
+    // ======================== DateLiteral ========================
+
+    @Test
+    public void testDateLiteralDateType() throws Exception {
+        DateLiteral d = new DateLiteral("2024-01-15", Type.DATEV2);
+        Assertions.assertEquals("2024-01-15",
+                V.visitDateLiteral(d, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+    }
+
+    @Test
+    public void testDateLiteralDatetimeType() throws Exception {
+        DateLiteral d = new DateLiteral("2024-01-15 10:30:00",
+                ScalarType.createDatetimeV2Type(0));
+        String result = V.visitDateLiteral(d, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("2024-01-15 10:30:00", result);
+    }
+
+    @Test
+    public void testDateLiteralInComplexType() throws Exception {
+        DateLiteral d = new DateLiteral("2024-01-15", Type.DATEV2);
+        Assertions.assertEquals("\"2024-01-15\"",
+                V.visitDateLiteral(d, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType()));
+    }
+
+    // ======================== CastExpr ========================
+
+    @Test
+    public void testCastExprQueryTopLevel() {
+        StringLiteral child = new StringLiteral("42");
+        CastExpr cast = new CastExpr(Type.BIGINT, child, false);
+        // Top-level query: returns getStringValue() of cast (empty string for 
CastExpr)
+        String result = V.visitCastExpr(cast, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals(cast.getStringValue(), result);
+    }
+
+    @Test
+    public void testCastExprInComplexType() {
+        StringLiteral child = new StringLiteral("42");
+        CastExpr cast = new CastExpr(Type.BIGINT, child, false);
+        // In complex type: delegates to child
+        String result = V.visitCastExpr(cast, 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType());
+        Assertions.assertEquals("\"42\"", result);
+    }
+
+    @Test
+    public void testCastExprStreamLoad() {
+        StringLiteral child = new StringLiteral("42");
+        CastExpr cast = new CastExpr(Type.BIGINT, child, false);
+        // Stream load: delegates to child
+        String result = V.visitCastExpr(cast, 
StringValueContext.forStreamLoad(FormatOptions.getDefault()));
+        Assertions.assertEquals("42", result);
+    }
+
+    // ======================== ArrayLiteral ========================
+
+    @Test
+    public void testArrayLiteralDefault() {
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.INT),
+                new IntLiteral(1), new IntLiteral(2), new IntLiteral(3));
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        // Default collectionDelim = ", "
+        Assertions.assertEquals("[1, 2, 3]", result);
+    }
+
+    @Test
+    public void testArrayLiteralHive() {
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.INT),
+                new IntLiteral(10), new IntLiteral(20));
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getForHive()));
+        // Hive collectionDelim = ","
+        Assertions.assertEquals("[10,20]", result);
+    }
+
+    @Test
+    public void testArrayLiteralWithStrings() {
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.VARCHAR),
+                new StringLiteral("abc"), new StringLiteral("def"));
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        // Strings get wrapped with nestedStringWrapper in complex context
+        Assertions.assertEquals("[\"abc\", \"def\"]", result);
+    }
+
+    @Test
+    public void testArrayLiteralWithNulls() {
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.INT),
+                new IntLiteral(1), new NullLiteral());
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        // NullLiteral in complex type returns nullFormat = "null"
+        Assertions.assertEquals("[1, null]", result);
+    }
+
+    @Test
+    public void testArrayLiteralEmpty() {
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.INT));
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("[]", result);
+    }
+
+    // ======================== MapLiteral ========================
+
+    @Test
+    public void testMapLiteralDefault() {
+        MapLiteral map = new MapLiteral(new MapType(Type.VARCHAR, Type.INT),
+                Arrays.asList(new StringLiteral("a"), new StringLiteral("b")),
+                Arrays.asList(new IntLiteral(1), new IntLiteral(2)));
+        String result = V.visitMapLiteral(map, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        // Default mapKeyDelim = ":", collectionDelim = ", ", strings wrapped
+        Assertions.assertEquals("{\"a\":1, \"b\":2}", result);
+    }
+
+    @Test
+    public void testMapLiteralPresto() {
+        MapLiteral map = new MapLiteral(new MapType(Type.VARCHAR, Type.INT),
+                Arrays.asList(new StringLiteral("x")),
+                Arrays.asList(new IntLiteral(42)));
+        String result = V.visitMapLiteral(map, 
StringValueContext.forQuery(FormatOptions.getForPresto()));
+        // Presto: mapKeyDelim = "=", wrapper = "" (empty)
+        Assertions.assertEquals("{x=42}", result);
+    }
+
+    // ======================== StructLiteral ========================
+
+    @Test
+    public void testStructLiteralQuery() throws Exception {
+        StructType structType = new StructType(
+                new StructField("name", Type.VARCHAR),
+                new StructField("age", Type.INT));
+        StructLiteral s = new StructLiteral(structType,
+                new StringLiteral("Alice"), new IntLiteral(30));
+        FormatOptions opts = FormatOptions.getDefault();
+        String result = V.visitStructLiteral(s, 
StringValueContext.forQuery(opts));
+        // Query mode: includes field names, mapKeyDelim=":"
+        Assertions.assertEquals("{\"name\":\"Alice\", \"age\":30}", result);
+    }
+
+    @Test
+    public void testStructLiteralStreamLoad() throws Exception {
+        StructType structType = new StructType(
+                new StructField("name", Type.VARCHAR),
+                new StructField("age", Type.INT));
+        StructLiteral s = new StructLiteral(structType,
+                new StringLiteral("Bob"), new IntLiteral(25));
+        FormatOptions opts = FormatOptions.getDefault();
+        String result = V.visitStructLiteral(s, 
StringValueContext.forStreamLoad(opts));
+        // Stream load mode: NO field names, children use query+complex context
+        Assertions.assertEquals("{\"Bob\", 25}", result);
+    }
+
+    // ======================== Default visitor (unoverridden Expr) 
========================
+
+    @Test
+    public void testDefaultVisitFallsBackToGetStringValue() {
+        IntLiteral i = new IntLiteral(42);
+        // IntLiteral has no specific visit method, falls through to default 
visit()
+        String result = V.visit(i, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("42", result);
+    }
+
+    // ======================== Context behavior ========================
+
+    @Test
+    public void testAsComplexTypeIdempotent() {
+        StringValueContext ctx = 
StringValueContext.forQuery(FormatOptions.getDefault()).asComplexType();
+        StringValueContext ctx2 = ctx.asComplexType();
+        // asComplexType on already-complex context returns same instance
+        Assertions.assertSame(ctx, ctx2);
+    }
+
+    @Test
+    public void testContextModeFlags() {
+        StringValueContext query = 
StringValueContext.forQuery(FormatOptions.getDefault());
+        Assertions.assertFalse(query.isForStreamLoad());
+        Assertions.assertFalse(query.isInComplexType());
+
+        StringValueContext stream = 
StringValueContext.forStreamLoad(FormatOptions.getDefault());
+        Assertions.assertTrue(stream.isForStreamLoad());
+        Assertions.assertFalse(stream.isInComplexType());
+
+        StringValueContext complex = query.asComplexType();
+        Assertions.assertFalse(complex.isForStreamLoad());
+        Assertions.assertTrue(complex.isInComplexType());
+    }
+
+    @Test
+    public void testAsQueryComplexType() {
+        StringValueContext streamCtx = 
StringValueContext.forStreamLoad(FormatOptions.getDefault());
+        StringValueContext queryComplex = streamCtx.asQueryComplexType();
+        // asQueryComplexType: forces query mode + complex type
+        Assertions.assertFalse(queryComplex.isForStreamLoad());
+        Assertions.assertTrue(queryComplex.isInComplexType());
+    }
+
+    // ======================== Nested collections ========================
+
+    @Test
+    public void testNestedArrayInArray() {
+        ArrayLiteral inner = new ArrayLiteral(new ArrayType(Type.INT),
+                new IntLiteral(1), new IntLiteral(2));
+        ArrayLiteral outer = new ArrayLiteral(new ArrayType(new 
ArrayType(Type.INT)), inner);
+        String result = V.visitArrayLiteral(outer, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("[[1, 2]]", result);
+    }
+
+    @Test
+    public void testArrayWithMapValues() {
+        MapLiteral map = new MapLiteral(new MapType(Type.VARCHAR, Type.INT),
+                Arrays.asList(new StringLiteral("k")),
+                Arrays.asList(new IntLiteral(1)));
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(new 
MapType(Type.VARCHAR, Type.INT)), map);
+        String result = V.visitArrayLiteral(arr, 
StringValueContext.forQuery(FormatOptions.getDefault()));
+        Assertions.assertEquals("[{\"k\":1}]", result);
+    }
+
+    // ======================== Level tracking ========================
+
+    @Test
+    public void testLevelResetAfterArray() {
+        FormatOptions opts = FormatOptions.getDefault();
+        Assertions.assertEquals(0, opts.level);
+        ArrayLiteral arr = new ArrayLiteral(new ArrayType(Type.INT), new 
IntLiteral(1));
+        V.visitArrayLiteral(arr, StringValueContext.forQuery(opts));
+        // level should be restored to 0 after visit
+        Assertions.assertEquals(0, opts.level);
+    }
+
+    @Test
+    public void testLevelResetAfterMap() {
+        FormatOptions opts = FormatOptions.getDefault();
+        MapLiteral map = new MapLiteral(new MapType(Type.VARCHAR, Type.INT),
+                Arrays.asList(new StringLiteral("k")),
+                Arrays.asList(new IntLiteral(1)));
+        V.visitMapLiteral(map, StringValueContext.forQuery(opts));
+        Assertions.assertEquals(0, opts.level);
+    }
+
+    @Test
+    public void testLevelResetAfterStruct() throws Exception {
+        FormatOptions opts = FormatOptions.getDefault();
+        StructType st = new StructType(new StructField("f", Type.INT));
+        StructLiteral s = new StructLiteral(st, new IntLiteral(1));
+        V.visitStructLiteral(s, StringValueContext.forQuery(opts));
+        Assertions.assertEquals(0, opts.level);
+    }
+
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/FloatLiteralTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/FloatLiteralTest.java
index 8d0ef45fdf5..14a12ed4b75 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/analysis/FloatLiteralTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/FloatLiteralTest.java
@@ -18,6 +18,7 @@
 package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
+import org.apache.doris.foundation.format.FormatOptions;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -26,79 +27,79 @@ public class FloatLiteralTest {
 
     @Test
     public void testDoubleGetStringValue() {
-        Assertions.assertEquals("0", new FloatLiteral(0d, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("0", new FloatLiteral(0.0, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-0", new FloatLiteral(-0d, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1", new FloatLiteral(1d, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1", new FloatLiteral(1.0, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1", new FloatLiteral(-1d, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1.554", new FloatLiteral(1.554, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("0.338", new FloatLiteral(0.338, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1", new FloatLiteral(-1.0, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1e+100", new FloatLiteral(1e100, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1e-100", new FloatLiteral(1e-100, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1000000000000000", new FloatLiteral(1.0E15, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1000000000000000", new FloatLiteral(-1.0E15, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1e+16", new FloatLiteral(1.0E16, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1e+16", new FloatLiteral(-1.0E16, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("0.0001", new FloatLiteral(0.0001, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1e-05", new FloatLiteral(0.00001, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1e+308", new FloatLiteral(1e308, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1e+308", new FloatLiteral(-1e308, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("Infinity", new 
FloatLiteral(Double.POSITIVE_INFINITY, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-Infinity", new 
FloatLiteral(Double.NEGATIVE_INFINITY, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("NaN", new FloatLiteral(Double.NaN, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1234567890123456", new 
FloatLiteral(1234567890123456.12345, Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1.234567890123457e+16", new 
FloatLiteral(12345678901234567.12345, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("0.0001234567890123457", new 
FloatLiteral(0.0001234567890123456789, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("1.234567890123456e-15", new 
FloatLiteral(0.000000000000001234567890123456, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("123.456", new FloatLiteral(123.456000, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("123", new FloatLiteral(123.000, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1234567890123456", new 
FloatLiteral(-1234567890123456.12345, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1.234567890123457e+16", new 
FloatLiteral(-12345678901234567.12345, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-0.0001234567890123457", new 
FloatLiteral(-0.0001234567890123456789, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-1.234567890123456e-15", new 
FloatLiteral(-0.000000000000001234567890123456, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-123.456", new FloatLiteral(-123.456000, 
Type.DOUBLE).getStringValueForQuery(null));
-        Assertions.assertEquals("-123", new FloatLiteral(-123.000, 
Type.DOUBLE).getStringValueForQuery(null));
+        Assertions.assertEquals("0", new FloatLiteral(0d, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0", new FloatLiteral(0.0, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-0", new FloatLiteral(-0d, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1", new FloatLiteral(1d, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1", new FloatLiteral(1.0, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1", new FloatLiteral(-1d, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.554", new FloatLiteral(1.554, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.338", new FloatLiteral(0.338, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1", new FloatLiteral(-1.0, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e+100", new FloatLiteral(1e100, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e-100", new FloatLiteral(1e-100, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1000000000000000", new FloatLiteral(1.0E15, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1000000000000000", new FloatLiteral(-1.0E15, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e+16", new FloatLiteral(1.0E16, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1e+16", new FloatLiteral(-1.0E16, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.0001", new FloatLiteral(0.0001, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e-05", new FloatLiteral(0.00001, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e+308", new FloatLiteral(1e308, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1e+308", new FloatLiteral(-1e308, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("Infinity", new 
FloatLiteral(Double.POSITIVE_INFINITY, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-Infinity", new 
FloatLiteral(Double.NEGATIVE_INFINITY, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("NaN", new FloatLiteral(Double.NaN, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1234567890123456", new 
FloatLiteral(1234567890123456.12345, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.234567890123457e+16", new 
FloatLiteral(12345678901234567.12345, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.0001234567890123457", new 
FloatLiteral(0.0001234567890123456789, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.234567890123456e-15", new 
FloatLiteral(0.000000000000001234567890123456, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("123.456", new FloatLiteral(123.456000, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("123", new FloatLiteral(123.000, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1234567890123456", new 
FloatLiteral(-1234567890123456.12345, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1.234567890123457e+16", new 
FloatLiteral(-12345678901234567.12345, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-0.0001234567890123457", new 
FloatLiteral(-0.0001234567890123456789, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1.234567890123456e-15", new 
FloatLiteral(-0.000000000000001234567890123456, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-123.456", new FloatLiteral(-123.456000, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-123", new FloatLiteral(-123.000, 
Type.DOUBLE).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
     }
 
     @Test
     public void testFloatGetStringValue() {
-        Assertions.assertEquals("0", new FloatLiteral(0d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("0", new FloatLiteral(0.0, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-0", new FloatLiteral(-0d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1", new FloatLiteral(1d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1", new FloatLiteral(1.0d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1", new FloatLiteral(-1d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1.554", new FloatLiteral(1.554d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("0.338", new FloatLiteral(0.338d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1", new FloatLiteral(-1.0d, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1e+38", new FloatLiteral(1e38, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1e-38", new FloatLiteral(1e-38, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1000000", new 
FloatLiteral(1.0E6).getStringValueForQuery(null));
-        Assertions.assertEquals("-1000000", new FloatLiteral(-1.0E6, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1e+07", new FloatLiteral(1.0E7, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1e+07", new FloatLiteral(-1.0E7, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("0.0001", new FloatLiteral(0.0001, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1e-05", new FloatLiteral(0.00001, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("3.402823e+38", new FloatLiteral((double) 
Float.MAX_VALUE, Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("Infinity", new FloatLiteral(1e39, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-Infinity", new FloatLiteral(-1e39, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("Infinity", new 
FloatLiteral(Double.POSITIVE_INFINITY, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-Infinity", new 
FloatLiteral(Double.NEGATIVE_INFINITY, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("NaN", new FloatLiteral(Double.NaN, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("123456.1", new FloatLiteral(123456.12345, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1234567", new FloatLiteral(1234567.12345, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1.234568e+07", new 
FloatLiteral(12345678.12345, Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("0.0001234568", new 
FloatLiteral(0.0001234567890123456789, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("1.234568e-15", new 
FloatLiteral(0.000000000000001234567890123456, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("123.456", new FloatLiteral(123.456000, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("123", new FloatLiteral(123.000, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1234567", new FloatLiteral(-1234567.12345, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1.234568e+07", new 
FloatLiteral(-12345678.12345, Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-0.0001234568", new 
FloatLiteral(-0.0001234567890123456789, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-1.234568e-15", new 
FloatLiteral(-0.000000000000001234567890123456, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-123.456", new FloatLiteral(-123.456000, 
Type.FLOAT).getStringValueForQuery(null));
-        Assertions.assertEquals("-123", new FloatLiteral(-123.000, 
Type.FLOAT).getStringValueForQuery(null));
+        Assertions.assertEquals("0", new FloatLiteral(0d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0", new FloatLiteral(0.0, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-0", new FloatLiteral(-0d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1", new FloatLiteral(1d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1", new FloatLiteral(1.0d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1", new FloatLiteral(-1d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.554", new FloatLiteral(1.554d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.338", new FloatLiteral(0.338d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1", new FloatLiteral(-1.0d, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e+38", new FloatLiteral(1e38, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e-38", new FloatLiteral(1e-38, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1000000", new 
FloatLiteral(1.0E6).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1000000", new FloatLiteral(-1.0E6, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e+07", new FloatLiteral(1.0E7, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1e+07", new FloatLiteral(-1.0E7, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.0001", new FloatLiteral(0.0001, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1e-05", new FloatLiteral(0.00001, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("3.402823e+38", new FloatLiteral((double) 
Float.MAX_VALUE, Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("Infinity", new FloatLiteral(1e39, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-Infinity", new FloatLiteral(-1e39, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("Infinity", new 
FloatLiteral(Double.POSITIVE_INFINITY, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-Infinity", new 
FloatLiteral(Double.NEGATIVE_INFINITY, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("NaN", new FloatLiteral(Double.NaN, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("123456.1", new FloatLiteral(123456.12345, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1234567", new FloatLiteral(1234567.12345, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.234568e+07", new 
FloatLiteral(12345678.12345, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("0.0001234568", new 
FloatLiteral(0.0001234567890123456789, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("1.234568e-15", new 
FloatLiteral(0.000000000000001234567890123456, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("123.456", new FloatLiteral(123.456000, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("123", new FloatLiteral(123.000, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1234567", new FloatLiteral(-1234567.12345, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1.234568e+07", new 
FloatLiteral(-12345678.12345, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-0.0001234568", new 
FloatLiteral(-0.0001234567890123456789, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-1.234568e-15", new 
FloatLiteral(-0.000000000000001234567890123456, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-123.456", new FloatLiteral(-123.456000, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
+        Assertions.assertEquals("-123", new FloatLiteral(-123.000, 
Type.FLOAT).accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(FormatOptions.getDefault())));
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/VarBinaryLiteralAnalysisTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/VarBinaryLiteralAnalysisTest.java
index c42a652e23b..5eae96bd964 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/analysis/VarBinaryLiteralAnalysisTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/analysis/VarBinaryLiteralAnalysisTest.java
@@ -46,9 +46,9 @@ public class VarBinaryLiteralAnalysisTest {
         Assertions.assertEquals("abc", lit.getStringValue());
 
         FormatOptions opts = FormatOptions.getDefault();
-        Assertions.assertEquals("\"abc\"", 
lit.getStringValueInComplexTypeForQuery(opts));
+        Assertions.assertEquals("\"abc\"", 
lit.accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(opts).asComplexType()));
         FormatOptions hive = FormatOptions.getForHive();
-        Assertions.assertEquals("\"abc\"", 
lit.getStringValueInComplexTypeForQuery(hive));
+        Assertions.assertEquals("\"abc\"", 
lit.accept(ExprToStringValueVisitor.INSTANCE, 
StringValueContext.forQuery(hive).asComplexType()));
     }
 
     @Test
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/VarBinaryLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/VarBinaryLiteralTest.java
index f584cc36c9f..01a03ad93f2 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/VarBinaryLiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/VarBinaryLiteralTest.java
@@ -18,8 +18,10 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.analysis.ExprToSqlVisitor;
+import org.apache.doris.analysis.ExprToStringValueVisitor;
 import org.apache.doris.analysis.ExprToThriftVisitor;
 import org.apache.doris.analysis.NullLiteral;
+import org.apache.doris.analysis.StringValueContext;
 import org.apache.doris.analysis.ToSqlParams;
 import org.apache.doris.analysis.VarBinaryLiteral;
 import org.apache.doris.foundation.format.FormatOptions;
@@ -51,11 +53,15 @@ public class VarBinaryLiteralTest {
 
         // nested string wrapper behavior (still wraps the plain string value)
         FormatOptions opts = FormatOptions.getDefault();
-        Assertions.assertEquals("\"hello\"", 
lit.getStringValueInComplexTypeForQuery(opts));
+        Assertions.assertEquals("\"hello\"",
+                lit.accept(ExprToStringValueVisitor.INSTANCE,
+                        StringValueContext.forQuery(opts).asComplexType()));
 
         // hive option uses same wrapper for nested by default
         FormatOptions hive = FormatOptions.getForHive();
-        Assertions.assertEquals("\"hello\"", 
lit.getStringValueInComplexTypeForQuery(hive));
+        Assertions.assertEquals("\"hello\"",
+                lit.accept(ExprToStringValueVisitor.INSTANCE,
+                        StringValueContext.forQuery(hive).asComplexType()));
     }
 
     @Test


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

Reply via email to