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 597a006707ed2c..c3758cbe68ce3c 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 @@ -29,13 +29,13 @@ import org.apache.doris.common.AnalysisException; import org.apache.doris.common.util.SqlUtils; import org.apache.doris.common.util.TimeUtils; +import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral; import org.apache.doris.nereids.trees.plans.commands.info.ColumnDefinition; import org.apache.doris.nereids.types.DataType; import com.google.common.base.Preconditions; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.ZonedDateTime; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -130,31 +130,30 @@ public long getCurrentTimeStampPrecision() { } public String getValue() { + return getValue(null); + } + + public String getValue(Type type) { if (isCurrentTimeStamp()) { - return LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' '); + return formatCurrentTimeStamp(0, type != null && type.isTimeStampTz()); } 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 formatCurrentTimeStamp(getCurrentTimeStampPrecision(), + type != null && type.isTimeStampTz()); } return value; } + + private String formatCurrentTimeStamp(long precision, boolean withTimeZone) { + ZonedDateTime currentDateTime = ZonedDateTime.now(TimeUtils.getTimeZone().toZoneId()); + if (!withTimeZone && precision == 0) { + return currentDateTime.toLocalDateTime().toString().replace('T', ' '); + } + int scale = Math.toIntExact(precision); + if (withTimeZone) { + return TimestampTzLiteral.formatDateTime(currentDateTime, scale); + } + return TimestampTzLiteral.formatDateTime(currentDateTime.toLocalDateTime(), scale); + } } // parameter initialized in constructor @@ -243,7 +242,7 @@ public ColumnDefinition translateToColumnDefinition() { if (defaultValue.defaultValueExprDef != null) { value = new org.apache.doris.nereids .trees.plans.commands.info.DefaultValue( - defaultValue.getValue(), + defaultValue.getValue(type), defaultValue.defaultValueExprDef.getExprName(), defaultValue.defaultValueExprDef.getPrecision()); } else { @@ -498,7 +497,7 @@ public String toSql() { public Column toColumn() { return new Column(name, this.type, isKey, aggregateType, isAllowNull, autoIncInitValue, defaultValue.value, comment, visible, defaultValue.defaultValueExprDef, Column.COLUMN_UNIQUE_ID_INIT_VALUE, - defaultValue.getValue(), -1, generatedColumnInfo.orElse(null), generatedColumnsThatReferToThis); + defaultValue.getValue(type), -1, generatedColumnInfo.orElse(null), generatedColumnsThatReferToThis); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java index f6a8c25a7daa2d..e7ab19d8e3d1d7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java @@ -30,6 +30,7 @@ import org.apache.doris.qe.ConnectContext; import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.util.Objects; /** @@ -454,6 +455,14 @@ public static Expression fromJavaDateType(LocalDateTime dateTime, int precision) (dateTime.getNano() / 1000) / value * value); } + public static String formatDateTime(LocalDateTime dateTime, int precision) { + return ((TimestampTzLiteral) fromJavaDateType(dateTime, precision)).getStringValue(); + } + + public static String formatDateTime(ZonedDateTime dateTime, int precision) { + return formatDateTime(dateTime.toLocalDateTime(), precision) + dateTime.getOffset(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java index 39e26090be65b3..76474739de6c55 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinition.java @@ -547,7 +547,7 @@ public Column translateToCatalogStyleForSchemaChange() { Column column = new Column(name, type.toCatalogDataType(), isKey, aggType, isNullable, autoIncInitValue, defaultValue.map(DefaultValue::getValue).orElse(null), comment, isVisible, defaultValue.map(DefaultValue::getDefaultValueExprDef).orElse(null), Column.COLUMN_UNIQUE_ID_INIT_VALUE, - defaultValue.map(DefaultValue::getRawValue).orElse(null), onUpdateDefaultValue.isPresent(), + defaultValue.map(value -> value.getRawValue(type)).orElse(null), onUpdateDefaultValue.isPresent(), onUpdateDefaultValue.map(DefaultValue::getDefaultValueExprDef).orElse(null), clusterKeyId, generatedColumnDesc.map(GeneratedColumnDesc::translateToInfo).orElse(null), generatedColumnsThatReferToThis, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/DefaultValue.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/DefaultValue.java index ad5f41ec2d1816..fa892a5ada1069 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/DefaultValue.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/DefaultValue.java @@ -21,9 +21,10 @@ import org.apache.doris.catalog.ScalarType; import org.apache.doris.common.util.TimeUtils; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral; +import org.apache.doris.nereids.types.DataType; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; +import java.time.ZonedDateTime; /** * default value of a column. @@ -114,32 +115,34 @@ public long getCurrentTimeStampPrecision() { * get string result of a default value expression. */ public String getRawValue() { + return getRawValue(null); + } + + /** + * get string result of a default value expression. + */ + public String getRawValue(DataType dataType) { if (isCurrentTimeStamp()) { - return LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' '); + return formatCurrentTimeStamp(0, dataType != null && dataType.isTimeStampTzType()); } 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 formatCurrentTimeStamp(getCurrentTimeStampPrecision(), + dataType != null && dataType.isTimeStampTzType()); } return value; } + private String formatCurrentTimeStamp(long precision, boolean withTimeZone) { + ZonedDateTime currentDateTime = ZonedDateTime.now(TimeUtils.getTimeZone().toZoneId()); + if (!withTimeZone && precision == 0) { + return currentDateTime.toLocalDateTime().toString().replace('T', ' '); + } + int scale = Math.toIntExact(precision); + if (withTimeZone) { + return TimestampTzLiteral.formatDateTime(currentDateTime, scale); + } + return TimestampTzLiteral.formatDateTime(currentDateTime.toLocalDateTime(), scale); + } + /** * get string value of a default value expression. */ diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinitionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinitionTest.java index f20925011a4c44..56f32a75a7a33f 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinitionTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/info/ColumnDefinitionTest.java @@ -17,9 +17,14 @@ package org.apache.doris.nereids.trees.plans.commands.info; +import org.apache.doris.catalog.Column; +import org.apache.doris.nereids.types.TimeStampTzType; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.Optional; + public class ColumnDefinitionTest { @Test @@ -33,4 +38,17 @@ public void testNameEquals() { boolean expected2 = false; Assertions.assertEquals(expected2, columnDefinition.nameEquals(otherColName2, false)); } + + @Test + public void testTranslateToCatalogStyleForSchemaChangeWithTimeStampTzCurrentTimestamp() { + ColumnDefinition columnDefinition = new ColumnDefinition("ts", TimeStampTzType.of(6), false, + null, true, Optional.of(DefaultValue.currentTimeStampDefaultValueWithPrecision(6L)), ""); + + Column column = columnDefinition.translateToCatalogStyleForSchemaChange(); + + Assertions.assertEquals("CURRENT_TIMESTAMP(6)", column.getDefaultValue()); + Assertions.assertNotNull(column.getRealDefaultValue()); + Assertions.assertTrue(column.getRealDefaultValue().matches( + "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{6}[+-]\\d{2}:\\d{2}")); + } } diff --git a/regression-test/suites/schema_change_p0/test_timestamptz_default_schema_change.groovy b/regression-test/suites/schema_change_p0/test_timestamptz_default_schema_change.groovy new file mode 100644 index 00000000000000..8029e5f1a10076 --- /dev/null +++ b/regression-test/suites/schema_change_p0/test_timestamptz_default_schema_change.groovy @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("test_timestamptz_default_schema_change", "p0") { + def getTableStatusSql = """ + SHOW ALTER TABLE COLUMN + WHERE IndexName='test_timestamptz_default_schema_change' + ORDER BY createtime DESC LIMIT 1 + """ + + sql "DROP TABLE IF EXISTS test_timestamptz_default_schema_change FORCE" + sql "SET time_zone = '+08:00'" + + sql """ + CREATE TABLE IF NOT EXISTS test_timestamptz_default_schema_change ( + id INT NOT NULL + ) + DUPLICATE KEY(id) + DISTRIBUTED BY HASH(id) BUCKETS 1 + PROPERTIES ( + "replication_num" = "1", + "light_schema_change" = "false" + ) + """ + + sql """ INSERT INTO test_timestamptz_default_schema_change VALUES (1), (2), (3) """ + + sql """ + ALTER TABLE test_timestamptz_default_schema_change + ADD COLUMN ts TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP(6) + """ + + waitForSchemaChangeDone { + sql getTableStatusSql + time 120 + } + + sql """ sync """ + + def rows = sql """ + SELECT id, CAST(ts AS STRING) AS ts_str + FROM test_timestamptz_default_schema_change + ORDER BY id + """ + + assertEquals(3, rows.size()) + rows.each { row -> + assertTrue(row[1] != null) + assertTrue(row[1].toString() ==~ /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}[+-]\d{2}:\d{2}/, + "Unexpected TIMESTAMPTZ value: ${row[1]}") + } +} \ No newline at end of file