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

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new b4837e4191a branch-4.0: [fix](function) fix date_format fe too many 
string input problem #56241 (#56509)
b4837e4191a is described below

commit b4837e4191a782b7f202f497019c839d4fb4eecc
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Sep 27 09:56:04 2025 +0800

    branch-4.0: [fix](function) fix date_format fe too many string input 
problem #56241 (#56509)
    
    Cherry-picked from #56241
    
    Co-authored-by: dwdwqfwe <[email protected]>
---
 .../executable/DateTimeExtractAndTransform.java    |  45 ++++-
 .../org/apache/doris/nereids/util/DateUtils.java   | 199 +++++++++++++++++++++
 .../nereids/rules/expression/FoldConstantTest.java |   6 +
 .../sql-functions/doc_date_functions_test.out      |   3 +
 .../sql-functions/doc_date_functions_test.groovy   |  32 ++++
 5 files changed, 279 insertions(+), 6 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
index a23826ca2e3..92b8a8d0e9a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
@@ -43,6 +43,8 @@ import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.DecimalV3Type;
 import org.apache.doris.nereids.util.DateUtils;
 
+import org.apache.commons.lang3.StringUtils;
+
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.DayOfWeek;
@@ -65,7 +67,8 @@ import java.util.Locale;
 
 /**
  * executable function:
- * year, quarter, month, week, dayOfYear, dayOfweek, dayOfMonth, hour, minute, 
second, microsecond
+ * year, quarter, month, week, dayOfYear, dayOfweek, dayOfMonth, hour, minute,
+ * second, microsecond
  */
 public class DateTimeExtractAndTransform {
 
@@ -324,30 +327,60 @@ public class DateTimeExtractAndTransform {
      */
     @ExecFunction(name = "date_format")
     public static Expression dateFormat(DateLiteral date, StringLikeLiteral 
format) {
+        if (StringUtils.trim(format.getValue()).length() > 128) {
+            throw new AnalysisException("The length of format string in 
date_format() function should not be greater"
+                    + " than 128.");
+        }
+        DateTimeV2Literal datetime = new DateTimeV2Literal(date.getYear(), 
date.getMonth(), date.getDay(), 0, 0, 0, 0);
         format = (StringLikeLiteral) 
SupportJavaDateFormatter.translateJavaFormatter(format);
-        return new 
VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
+        return new 
VarcharLiteral(DateUtils.dateTimeFormatterChecklength(format.getValue(), 
datetime).format(
                 java.time.LocalDate.of(((int) date.getYear()), ((int) 
date.getMonth()), ((int) date.getDay()))));
     }
 
+    /**
+     * datetime arithmetic function date-format
+     */
     @ExecFunction(name = "date_format")
     public static Expression dateFormat(DateTimeLiteral date, 
StringLikeLiteral format) {
+        if (StringUtils.trim(format.getValue()).length() > 128) {
+            throw new AnalysisException("The length of format string in 
date_format() function should not be greater"
+                    + " than 128.");
+        }
+        DateTimeV2Literal datetime = new DateTimeV2Literal(date.getYear(), 
date.getMonth(), date.getDay(),
+                date.getHour(), date.getMinute(), date.getSecond(), 
date.getMicroSecond());
         format = (StringLikeLiteral) 
SupportJavaDateFormatter.translateJavaFormatter(format);
-        return new 
VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
+        return new 
VarcharLiteral(DateUtils.dateTimeFormatterChecklength(format.getValue(), 
datetime).format(
                 java.time.LocalDateTime.of(((int) date.getYear()), ((int) 
date.getMonth()), ((int) date.getDay()),
-                        ((int) date.getHour()), ((int) date.getMinute()), 
((int) date.getSecond()))));
+                        ((int) date.getHour()), ((int) date.getMinute()), 
((int) date.getSecond()),
+                        ((int) date.getMicroSecond() * 1000))));
     }
 
+    /**
+     * datetime arithmetic function date-format
+     */
     @ExecFunction(name = "date_format")
     public static Expression dateFormat(DateV2Literal date, StringLikeLiteral 
format) {
+        if (StringUtils.trim(format.getValue()).length() > 128) {
+            throw new AnalysisException("The length of format string in 
date_format() function should not be greater"
+                    + " than 128.");
+        }
+        DateTimeV2Literal datetime = new DateTimeV2Literal(date.getYear(), 
date.getMonth(), date.getDay(), 0, 0, 0, 0);
         format = (StringLikeLiteral) 
SupportJavaDateFormatter.translateJavaFormatter(format);
-        return new 
VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
+        return new 
VarcharLiteral(DateUtils.dateTimeFormatterChecklength(format.getValue(), 
datetime).format(
                 java.time.LocalDate.of(((int) date.getYear()), ((int) 
date.getMonth()), ((int) date.getDay()))));
     }
 
+    /**
+     * datetime arithmetic function date-format
+     */
     @ExecFunction(name = "date_format")
     public static Expression dateFormat(DateTimeV2Literal date, 
StringLikeLiteral format) {
+        if (StringUtils.trim(format.getValue()).length() > 128) {
+            throw new AnalysisException("The length of format string in 
date_format() function should not be greater"
+                    + " than 128.");
+        }
         format = (StringLikeLiteral) 
SupportJavaDateFormatter.translateJavaFormatter(format);
-        return new 
VarcharLiteral(DateUtils.dateTimeFormatter(format.getValue()).format(
+        return new 
VarcharLiteral(DateUtils.dateTimeFormatterChecklength(format.getValue(), 
date).format(
                 java.time.LocalDateTime.of(((int) date.getYear()), ((int) 
date.getMonth()), ((int) date.getDay()),
                         ((int) date.getHour()), ((int) date.getMinute()), 
((int) date.getSecond()),
                         ((int) date.getMicroSecond() * 1000))));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
index 047b8d43e10..ea05d2d3c1a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.util;
 
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableSet;
@@ -46,6 +47,204 @@ public class DateUtils {
             "%Y-%m-%d %T", "%Y%m%d", "%Y%m");
     private static final WeekFields weekFields = 
WeekFields.of(DayOfWeek.SUNDAY, 7);
 
+    /**
+     * get the length of day of week string
+     */
+    public static int dayOfWeekLength(int dayOfWeek) {
+        if (dayOfWeek == 1 || dayOfWeek == 7 || dayOfWeek == 5) {
+            return 6;
+        } else if (dayOfWeek == 2) {
+            return 7;
+        } else if (dayOfWeek == 6 || dayOfWeek == 4) {
+            return 8;
+        } else {
+            return 10;
+        }
+    }
+
+    /**
+     * get the length of month string
+     */
+    public static int monthLength(long month) {
+        if (month == 5) {
+            return 3;
+        } else if (month == 6 || month == 7) {
+            return 4;
+        } else if (month == 4 || month == 3) {
+            return 5;
+        } else if (month == 8) {
+            return 6;
+        } else if (month == 10 || month == 1) {
+            return 7;
+        } else if (month == 2 || month == 11 || month == 12) {
+            return 8;
+        } else {
+            return 9;
+        }
+    }
+
+    /**
+     * format builder with length check.
+     * for some pattern, the length of result string is not fixed,
+     * so we need to check the length of result string to avoid parse error.
+     */
+    public static DateTimeFormatter dateTimeFormatterChecklength(String 
pattern, DateTimeV2Literal date)
+            throws AnalysisException {
+        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+        boolean escaped = false;
+        int length = 0;
+        for (int i = 0; i < pattern.length(); i++) {
+            char character = pattern.charAt(i);
+            if (length > 100) {
+                throw new AnalysisException("Date format string is too long");
+            }
+            if (escaped) {
+                switch (character) {
+                    case 'a': // %a Abbreviated weekday name (Sun..Sat)
+                        builder.appendText(ChronoField.DAY_OF_WEEK, 
TextStyle.SHORT);
+                        length += 3;
+                        break;
+                    case 'b': // %b Abbreviated month name (Jan..Dec)
+                        builder.appendText(ChronoField.MONTH_OF_YEAR, 
TextStyle.SHORT);
+                        length += 3;
+                        break;
+                    case 'c': // %c Month, numeric (0..12)
+                        builder.appendValue(ChronoField.MONTH_OF_YEAR);
+                        if (date.getMonth() < 10) {
+                            length += 1;
+                        } else {
+                            length += 2;
+                        }
+                        break;
+                    case 'd': // %d Day of the month, numeric (00..31)
+                        builder.appendValue(ChronoField.DAY_OF_MONTH, 2);
+                        length += 2;
+                        break;
+                    case 'e': // %e Day of the month, numeric (0..31)
+                        builder.appendValue(ChronoField.DAY_OF_MONTH);
+                        if (date.getDay() < 10) {
+                            length += 1;
+                        } else {
+                            length += 2;
+                        }
+                        break;
+                    case 'H': // %H Hour (00..23)
+                        builder.appendValue(ChronoField.HOUR_OF_DAY, 2);
+                        length += 2;
+                        break;
+                    case 'h': // %h Hour (01..12)
+                    case 'I': // %I Hour (01..12)
+                        builder.appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2);
+                        length += 2;
+                        break;
+                    case 'i': // %i Minutes, numeric (00..59)
+                        builder.appendValue(ChronoField.MINUTE_OF_HOUR, 2);
+                        length += 2;
+                        break;
+                    case 'j': // %j Day of year (001..366)
+                        builder.appendValue(ChronoField.DAY_OF_YEAR, 3);
+                        length += 3;
+                        break;
+                    case 'k': // %k Hour (0..23)
+                        builder.appendValue(ChronoField.HOUR_OF_DAY);
+                        length += 2;
+                        break;
+                    case 'l': // %l Hour (1..12)
+                        builder.appendValue(ChronoField.CLOCK_HOUR_OF_AMPM);
+                        if ((date.getHour() % 24 + 11) % 12 + 1 >= 10) {
+                            length += 2;
+                        } else {
+                            length += 1;
+                        }
+                        break;
+                    case 'M': // %M Month name (January..December)
+                        builder.appendText(ChronoField.MONTH_OF_YEAR, 
TextStyle.FULL);
+                        length += monthLength(date.getMonth());
+                        break;
+                    case 'm': // %m Month, numeric (00..12)
+                        builder.appendValue(ChronoField.MONTH_OF_YEAR, 2);
+                        length += 2;
+                        break;
+                    case 'p': // %p AM or PM
+                        builder.appendText(ChronoField.AMPM_OF_DAY);
+                        length += 2;
+                        break;
+                    case 'r': // %r Time, 12-hour (hh:mm:ss followed by AM or 
PM)
+                        builder.appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2)
+                                .appendLiteral(':')
+                                .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
+                                .appendLiteral(':')
+                                .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+                                .appendLiteral(' ')
+                                .appendText(ChronoField.AMPM_OF_DAY, 
TextStyle.FULL)
+                                .toFormatter();
+                        length += 11;
+                        break;
+                    case 'S': // %S Seconds (00..59)
+                    case 's': // %s Seconds (00..59)
+                        builder.appendValue(ChronoField.SECOND_OF_MINUTE, 2);
+                        length += 2;
+                        break;
+                    case 'T': // %T Time, 24-hour (HH:mm:ss)
+                        builder.appendPattern("HH:mm:ss");
+                        length += 8;
+                        break;
+                    case 'V': // %V Week (01..53), where Sunday is the first 
day of the week; used with %X
+                        builder.appendValue(weekFields.weekOfWeekBasedYear(), 
2);
+                        length += 2;
+                        break;
+                    case 'v': // %v Week (01..53), where Monday is the first 
day of the week; used with %x
+                        length += 2;
+                        builder.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 
2);
+                        break;
+                    case 'W': // %W Weekday name (Sunday..Saturday)
+                        builder.appendText(ChronoField.DAY_OF_WEEK, 
TextStyle.FULL);
+                        length += dayOfWeekLength(date.getDayOfWeek());
+                        break;
+                    case 'x': // %x Year for the week where Monday is the 
first day of the week,
+                        builder.appendValue(IsoFields.WEEK_BASED_YEAR, 4);
+                        length += 4;
+                        break;
+                    case 'X':
+                        builder.appendValue(weekFields.weekBasedYear(), 4, 10, 
SignStyle.EXCEEDS_PAD);
+                        length += 4;
+                        break;
+                    case 'Y': // %Y Year, numeric, four digits
+                        // %X Year for the week, where Sunday is the first day 
of the week,
+                        // numeric, four digits; used with %v
+                        builder.appendValue(ChronoField.YEAR, 4);
+                        length += 4;
+                        break;
+                    case 'y': // %y Year, numeric (two digits)
+                        builder.appendValueReduced(ChronoField.YEAR, 2, 2, 
1970);
+                        length += 2;
+                        break;
+                    // TODO(Gabriel): support microseconds in date literal
+                    case 'D': // %D Day of the month with English suffix (0th, 
1st, 2nd, 3rd, …)
+                    case 'f': // %f Microseconds (000000..999999)
+                    case 'U': // %U Week (00..53), where Sunday is the first 
day of the week
+                    case 'u': // %u Week (00..53), where Monday is the first 
day of the week
+                    case 'w': // %w Day of the week (0=Sunday..6=Saturday)
+                        throw new AnalysisException(String.format("%%%s not 
supported in date format string",
+                                character));
+                    case '%': // %% A literal "%" character
+                        builder.appendLiteral('%');
+                        break;
+                    default: // %<x> The literal character represented by <x>
+                        builder.appendLiteral(character);
+                        length += 1;
+                        break;
+                }
+                escaped = false;
+            } else if (character == '%') {
+                escaped = true;
+            } else {
+                builder.appendLiteral(character);
+            }
+        }
+        return 
builder.toFormatter(Locale.US).withResolverStyle(ResolverStyle.STRICT);
+    }
+
     /**
      * format builder.
      */
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
index 570534584ec..0ba1f18c054 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
@@ -128,6 +128,7 @@ import org.apache.doris.nereids.types.VarcharType;
 import org.apache.doris.nereids.util.MemoTestUtils;
 
 import com.google.common.collect.ImmutableList;
+import org.apache.commons.lang3.StringUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -432,6 +433,11 @@ class FoldConstantTest extends ExpressionRewriteTestHelper 
{
         rewritten = executor.rewrite(d, context);
         Assertions.assertEquals(new VarcharLiteral("01 01 01"), rewritten);
 
+        d = new DateFormat(DateV2Literal.fromJavaDateType(LocalDateTime.of(1, 
1, 1, 1, 1, 1)),
+                        StringLiteral.of(StringUtils.repeat("s", 128) + "   
"));
+
+        rewritten = executor.rewrite(d, context);
+        Assertions.assertEquals(new VarcharLiteral(StringUtils.repeat("s", 
128) + "   "), rewritten);
         DateTrunc t = new 
DateTrunc(DateTimeV2Literal.fromJavaDateType(LocalDateTime.of(1, 1, 1, 1, 1, 
1)),
                 StringLiteral.of("week"));
         rewritten = executor.rewrite(t, context);
diff --git 
a/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out 
b/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out
index 3eea82fd705..7807b4c5e2b 100644
--- 
a/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out
+++ 
b/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out
@@ -113,6 +113,9 @@ Sunday October 2009
 -- !date_format_4 --
 01/01/1999 00:00:00
 
+-- !date_format_5 --
+111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
 -- !date_format_5 --
 Sun Oct 10
 
diff --git 
a/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy
 
b/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy
index 5e145b2b4b7..23f285ad291 100644
--- 
a/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy
+++ 
b/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy
@@ -154,6 +154,38 @@ suite("doc_date_functions_test") {
     qt_date_format_2 """SELECT DATE_FORMAT('2007-10-04 22:23:00', 
'%H:%i:%s')"""
     qt_date_format_3 """SELECT DATE_FORMAT('1999-01-01', '%Y-%m-%d')"""
     qt_date_format_4 """SELECT DATE_FORMAT('1999-01-01 00:00:00', '%d/%m/%Y 
%H:%i:%s')"""
+
+    qt_date_format_5 """select date_format('2022-11-13 
11:12:12',repeat('%l',51));"""
+
+    test {
+        sql """select date_format('2022-11-13 11:12:12',repeat('%l',52));"""
+        exception "Operation date_format of 142335814007783424, 
%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l
 is invalid"
+    }
+
+    test {
+        sql """select date_format('2023-11-13 23:00:00' ,repeat('%l',53));"""
+        exception "Operation date_format of 142406233473679360, 
%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l%l
 is invalid"
+    }
+
+    test {
+        sql """SELECT DATE_FORMAT('1222-12-12', repeat('s',129))"""
+        exception  "Operation date_format of invalid or oversized format is 
invalid"
+    }
+
+    test {
+        sql """select date_format('2022-11-13',repeat('%I',52));"""
+        exception "Operation date_format of 142335765945253888, 
%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I%I
 is invalid"
+    }
+
+    test {
+        sql """select date_format('2022-11-13',repeat('%M',15));"""
+        exception "Operation date_format of 142335765945253888, 
%M%M%M%M%M%M%M%M%M%M%M%M%M%M%M is invalid"
+    }
+
+    test {
+        sql """select date_format('2022-11-13',repeat('%p',52));"""
+        exception "Operation date_format of 142335765945253888, 
%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
 is invalid"
+    }
     
     // 特殊格式符测试
     qt_date_format_5 """SELECT DATE_FORMAT('2009-10-04', '%a %b %c')"""


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

Reply via email to