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

zykkk 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 d3be10ee58 [improvement](column) Support for the default value of 
current_timestamp in microsecond (#21487)
d3be10ee58 is described below

commit d3be10ee5854480980722d442d2c583aaab83860
Author: zy-kkk <zhongy...@gmail.com>
AuthorDate: Tue Jul 11 14:04:13 2023 +0800

    [improvement](column) Support for the default value of current_timestamp in 
microsecond (#21487)
---
 fe/fe-core/src/main/cup/sql_parser.cup             |  4 +
 .../java/org/apache/doris/analysis/ColumnDef.java  | 70 ++++++++++++++++-
 .../apache/doris/analysis/DefaultValueExprDef.java | 49 +++++++++++-
 .../apache/doris/datasource/InternalCatalog.java   |  8 +-
 .../data/correctness_p0/test_current_timestamp.out |  9 +++
 .../correctness_p0/test_current_timestamp.groovy   | 89 +++++++++++++++++++++-
 6 files changed, 222 insertions(+), 7 deletions(-)

diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index 20d722c6a2..e3f0508543 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -3518,6 +3518,10 @@ opt_default_value ::=
     {:
         RESULT = ColumnDef.DefaultValue.CURRENT_TIMESTAMP_DEFAULT_VALUE;
     :}
+    | KW_DEFAULT KW_CURRENT_TIMESTAMP LPAREN INTEGER_LITERAL:precision RPAREN
+    {:
+        RESULT = 
ColumnDef.DefaultValue.currentTimeStampDefaultValueWithPrecision(precision);
+    :}
     ;
 
 opt_is_key ::=
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ColumnDef.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ColumnDef.java
index b8c69b438b..2ae7a04335 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ColumnDef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ColumnDef.java
@@ -36,6 +36,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -88,6 +89,12 @@ public class ColumnDef {
             this.defaultValueExprDef = new DefaultValueExprDef(exprName);
         }
 
+        public DefaultValue(boolean isSet, String value, String exprName, Long 
precision) {
+            this.isSet = isSet;
+            this.value = value;
+            this.defaultValueExprDef = new DefaultValueExprDef(exprName, 
precision);
+        }
+
         // default "CURRENT_TIMESTAMP", only for DATETIME type
         public static String CURRENT_TIMESTAMP = "CURRENT_TIMESTAMP";
         public static String NOW = "now";
@@ -104,14 +111,60 @@ public class ColumnDef {
         // default "value", "[]" means empty array
         public static DefaultValue ARRAY_EMPTY_DEFAULT_VALUE = new 
DefaultValue(true, "[]");
 
+        public static DefaultValue 
currentTimeStampDefaultValueWithPrecision(Long precision) {
+            if (precision > ScalarType.MAX_DATETIMEV2_SCALE || precision < 0) {
+                throw new IllegalArgumentException("column's default value 
current_timestamp"
+                            + " precision must be between 0 and 6");
+            }
+            if (precision == 0) {
+                return new DefaultValue(true, CURRENT_TIMESTAMP, NOW);
+            }
+            String value = CURRENT_TIMESTAMP + "(" + precision + ")";
+            String exprName = NOW;
+            return new DefaultValue(true, value, exprName, precision);
+        }
+
         public boolean isCurrentTimeStamp() {
             return "CURRENT_TIMESTAMP".equals(value) && 
NOW.equals(defaultValueExprDef.getExprName());
         }
 
+        public boolean isCurrentTimeStampWithPrecision() {
+            return defaultValueExprDef != null && 
value.startsWith(CURRENT_TIMESTAMP + "(")
+                    && NOW.equals(defaultValueExprDef.getExprName());
+        }
+
+        public long getCurrentTimeStampPrecision() {
+            if (isCurrentTimeStampWithPrecision()) {
+                return 
Long.parseLong(value.substring(CURRENT_TIMESTAMP.length() + 1, value.length() - 
1));
+            }
+            return 0;
+        }
+
         public String getValue() {
-            return isCurrentTimeStamp()
-                    ? 
LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' 
')
-                    : value;
+            if (isCurrentTimeStamp()) {
+                return 
LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' 
');
+            } else if (isCurrentTimeStampWithPrecision()) {
+                long precision = getCurrentTimeStampPrecision();
+                String format = "yyyy-MM-dd HH:mm:ss";
+                if (precision == 0) {
+                    return 
LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' 
');
+                } else if (precision == 1) {
+                    format = "yyyy-MM-dd HH:mm:ss.S";
+                } else if (precision == 2) {
+                    format = "yyyy-MM-dd HH:mm:ss.SS";
+                } else if (precision == 3) {
+                    format = "yyyy-MM-dd HH:mm:ss.SSS";
+                } else if (precision == 4) {
+                    format = "yyyy-MM-dd HH:mm:ss.SSSS";
+                } else if (precision == 5) {
+                    format = "yyyy-MM-dd HH:mm:ss.SSSSS";
+                } else if (precision == 6) {
+                    format = "yyyy-MM-dd HH:mm:ss.SSSSSS";
+                }
+                return LocalDateTime.now(TimeUtils.getTimeZone().toZoneId())
+                        .format(DateTimeFormatter.ofPattern(format));
+            }
+            return value;
         }
     }
 
@@ -459,6 +512,17 @@ public class ColumnDef {
                     new DateLiteral(defaultValue, scalarType);
                 } else {
                     if 
(defaultValueExprDef.getExprName().equals(DefaultValue.NOW)) {
+                        if (defaultValueExprDef.getPrecision() != null) {
+                            Long defaultValuePrecision = 
defaultValueExprDef.getPrecision();
+                            String typeStr = scalarType.toString();
+                            int typePrecision =
+                                    
Integer.parseInt(typeStr.substring(typeStr.indexOf("(") + 1, 
typeStr.indexOf(")")));
+                            if (defaultValuePrecision > typePrecision) {
+                                typeStr = typeStr.replace("V2", "");
+                                throw new AnalysisException("default value 
precision: " + defaultValue
+                                        + " can not be greater than type 
precision: " + typeStr);
+                            }
+                        }
                         break;
                     } else {
                         throw new AnalysisException("date literal [" + 
defaultValue + "] is invalid");
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DefaultValueExprDef.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DefaultValueExprDef.java
index adccf078c6..dcaa944e0d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/DefaultValueExprDef.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DefaultValueExprDef.java
@@ -22,29 +22,52 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.io.Text;
+import org.apache.doris.common.io.Writable;
+import org.apache.doris.persist.gson.GsonPostProcessable;
+import org.apache.doris.persist.gson.GsonUtils;
 
+import com.google.common.collect.Lists;
 import com.google.gson.annotations.SerializedName;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.List;
+
 /**
  * This def used for column which defaultValue is an expression.
  */
-public class DefaultValueExprDef {
+public class DefaultValueExprDef implements Writable, GsonPostProcessable {
     private static final Logger LOG = 
LogManager.getLogger(DefaultValueExprDef.class);
     @SerializedName("exprName")
     private String exprName;
+    @SerializedName("precision")
+    private Long precision;
+
 
     public DefaultValueExprDef(String exprName) {
         this.exprName = exprName;
     }
 
+    public DefaultValueExprDef(String exprName, Long precision) {
+        this.exprName = exprName;
+        this.precision = precision;
+    }
+
     /**
      * generate a FunctionCallExpr
      * @return FunctionCallExpr of exprName
      */
     public FunctionCallExpr getExpr(Type type) {
-        FunctionCallExpr expr = new FunctionCallExpr(exprName, new 
FunctionParams(null));
+        List<Expr> exprs = null;
+        if (precision != null) {
+            exprs = Lists.newArrayList();
+            exprs.add(new IntLiteral(precision));
+        }
+        FunctionCallExpr expr = new FunctionCallExpr(exprName, new 
FunctionParams(exprs));
         try {
             expr.analyzeImplForDefaultValue(type);
         } catch (AnalysisException e) {
@@ -56,4 +79,26 @@ public class DefaultValueExprDef {
     public String getExprName() {
         return exprName;
     }
+
+    public Long getPrecision() {
+        return precision;
+    }
+
+    @Override
+    public void write(DataOutput out) throws IOException {
+        String json = GsonUtils.GSON.toJson(this);
+        Text.writeString(out, json);
+    }
+
+    public static DefaultValueExprDef read(DataInput in) throws IOException {
+        String json = Text.readString(in);
+        return GsonUtils.GSON.fromJson(json, DefaultValueExprDef.class);
+    }
+
+    @Override
+    public void gsonPostProcess() throws IOException {
+        if (precision == null) {
+            precision = 0L;
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
index 6998338aaa..ec2993ab88 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java
@@ -1230,8 +1230,14 @@ public class InternalCatalog implements 
CatalogIf<Database> {
                     boolean setDefault = 
StringUtils.isNotBlank(column.getDefaultValue());
                     DefaultValue defaultValue;
                     if (column.getDefaultValueExprDef() != null) {
-                        defaultValue = new DefaultValue(setDefault, 
column.getDefaultValue(),
+                        if (column.getDefaultValueExprDef().getPrecision() != 
null) {
+                            defaultValue = new DefaultValue(setDefault, 
column.getDefaultValue(),
+                                column.getDefaultValueExprDef().getExprName(),
+                                
column.getDefaultValueExprDef().getPrecision());
+                        } else {
+                            defaultValue = new DefaultValue(setDefault, 
column.getDefaultValue(),
                                 column.getDefaultValueExprDef().getExprName());
+                        }
                     } else {
                         defaultValue = new DefaultValue(setDefault, 
column.getDefaultValue());
                     }
diff --git a/regression-test/data/correctness_p0/test_current_timestamp.out 
b/regression-test/data/correctness_p0/test_current_timestamp.out
index 067ef6f5c6..e66b4f88b9 100644
--- a/regression-test/data/correctness_p0/test_current_timestamp.out
+++ b/regression-test/data/correctness_p0/test_current_timestamp.out
@@ -42,3 +42,12 @@
 -- !stream_load_json6 --
 2
 
+-- !insert_sql --
+1
+
+-- !insert_sql --
+1
+
+-- !select_sql --
+2
+
diff --git 
a/regression-test/suites/correctness_p0/test_current_timestamp.groovy 
b/regression-test/suites/correctness_p0/test_current_timestamp.groovy
index dcadc4c0a0..e4673259e6 100644
--- a/regression-test/suites/correctness_p0/test_current_timestamp.groovy
+++ b/regression-test/suites/correctness_p0/test_current_timestamp.groovy
@@ -53,7 +53,7 @@ suite("test_current_timestamp") {
         DISTRIBUTED BY HASH(id)
         PROPERTIES("replication_num" = "1");
     """
-    
+
     // test insert into.
     sql " insert into ${tableName} (id,name,dt_0,dt_2,dt_4,dt_6) values 
(1,'aa',current_timestamp(),current_timestamp(),current_timestamp(),current_timestamp());
 "
     sql " insert into ${tableName} (id,name,dt_0,dt_2,dt_4,dt_6) values 
(2,'bb',current_timestamp(),current_timestamp(),current_timestamp(),current_timestamp());
 "
@@ -128,4 +128,91 @@ suite("test_current_timestamp") {
 
     qt_stream_load_json5 """ select id, name from ${tableName2} where dt_1 is 
not NULL order by id; """
     qt_stream_load_json6 """ select count(*) from ${tableName2} where dt_2 is 
not NULL; """
+
+    def tableName3 = "test_current_timestamp_ms"
+
+    sql """ DROP TABLE IF EXISTS ${tableName3} """
+    sql """
+        CREATE TABLE IF NOT EXISTS ${tableName3}
+        (
+            id TINYINT,
+            name CHAR(10) NOT NULL DEFAULT "zs",
+            dt_2 DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3)
+        )
+        COMMENT "test current_timestamp table3"
+        DISTRIBUTED BY HASH(id)
+        PROPERTIES("replication_num" = "1");
+    """
+
+    // test insert into.
+    qt_insert_sql " insert into ${tableName3} (id,name,dt_2) values 
(1,'aa',current_timestamp(3)); "
+    qt_insert_sql " insert into ${tableName3} (id,name) values (2,'bb'); "
+
+    qt_select_sql """ select count(*) from ${tableName3} where to_date(dt_2) = 
to_date(current_timestamp(3)); """
+
+    // test csv stream load.
+    streamLoad {
+        table "${tableName3}"
+
+        set 'column_separator', ','
+        set 'columns', 'id, name, dt_2 = current_timestamp(3)'
+
+        file 'test_current_timestamp_streamload.csv'
+
+        time 10000 // limit inflight 10s
+    }
+
+    sql "sync"
+
+    // test json stream load
+    streamLoad {
+        table "${tableName3}"
+
+        set 'columns', 'id, name, dt_2 = current_timestamp(3)'
+        set 'format', 'json'
+        set 'read_json_by_line', 'true'
+        set 'strip_outer_array', 'false'
+
+        file 'test_current_timestamp_streamload.json'
+
+        time 10000 // limit inflight 10s
+    }
+
+    sql "sync"
+
+    // test create
+    def tableName4 = "test_current_timestamp_ms2"
+    test {
+        sql """ DROP TABLE IF EXISTS ${tableName4} """
+        sql """
+            CREATE TABLE IF NOT EXISTS ${tableName4}
+            (
+                id TINYINT,
+                name CHAR(10) NOT NULL DEFAULT "zs",
+                dt_2 DATETIME(1) DEFAULT CURRENT_TIMESTAMP(3)
+            )
+            COMMENT "test current_timestamp table4"
+            DISTRIBUTED BY HASH(id)
+            PROPERTIES("replication_num" = "1");
+        """
+        exception "errCode = 2, detailMessage = default value precision: 
CURRENT_TIMESTAMP(3) can not be greater than type precision: DATETIME(1)"
+    }
+
+    // test create
+    def tableName5 = "test_current_timestamp_ms3"
+    test {
+        sql """ DROP TABLE IF EXISTS ${tableName5} """
+        sql """
+            CREATE TABLE IF NOT EXISTS ${tableName5}
+            (
+                id TINYINT,
+                name CHAR(10) NOT NULL DEFAULT "zs",
+                dt_2 DATETIME(6) DEFAULT CURRENT_TIMESTAMP(7)
+            )
+            COMMENT "test current_timestamp table5"
+            DISTRIBUTED BY HASH(id)
+            PROPERTIES("replication_num" = "1");
+        """
+        exception "errCode = 2, detailMessage = Internal Error, maybe syntax 
error or this is a bug: column's default value current_timestamp precision must 
be between 0 and 6"
+    }
  }


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

Reply via email to