[Bug](CURRENT_TIMESTAMP) Fix wrong default value after schema change (#16364)

* [Bug](CURRENT_TIMESTAMP) Fix wrong default value after schema change

* update

* update
This commit is contained in:
Gabriel
2023-02-03 17:06:24 +08:00
committed by GitHub
parent 4df70becb9
commit 3f4ca3da32
7 changed files with 107 additions and 13 deletions

View File

@ -28,11 +28,14 @@ import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.FeNameFormat;
import org.apache.doris.common.util.TimeUtils;
import com.google.common.base.Preconditions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.time.LocalDateTime;
// Column definition which is generated by SQL syntax parser
// Syntax:
// name type [key] [agg_type] [NULL | NOT NULL] [DEFAULT default_value] [comment]
@ -95,6 +98,16 @@ public class ColumnDef {
public static DefaultValue HLL_EMPTY_DEFAULT_VALUE = new DefaultValue(true, ZERO);
// default "value", "0" means empty bitmap
public static DefaultValue BITMAP_EMPTY_DEFAULT_VALUE = new DefaultValue(true, ZERO);
public boolean isCurrentTimeStamp() {
return "CURRENT_TIMESTAMP".equals(value) && NOW.equals(defaultValueExprDef.getExprName());
}
public String getValue() {
return isCurrentTimeStamp()
? LocalDateTime.now(TimeUtils.getTimeZone().toZoneId()).toString().replace('T', ' ')
: value;
}
}
// parameter initialized in constructor
@ -362,12 +375,12 @@ public class ColumnDef {
break;
case DATE:
case DATEV2:
new DateLiteral(defaultValue, ScalarType.getDefaultDateType(type));
new DateLiteral(defaultValue, scalarType);
break;
case DATETIME:
case DATETIMEV2:
if (defaultValueExprDef == null) {
new DateLiteral(defaultValue, ScalarType.getDefaultDateType(type));
new DateLiteral(defaultValue, scalarType);
} else {
if (defaultValueExprDef.getExprName().equals(DefaultValue.NOW)) {
break;
@ -424,7 +437,8 @@ public class ColumnDef {
public Column toColumn() {
return new Column(name, typeDef.getType(), isKey, aggregateType, isAllowNull, defaultValue.value, comment,
visible, defaultValue.defaultValueExprDef, Column.COLUMN_UNIQUE_ID_INIT_VALUE);
visible, defaultValue.defaultValueExprDef, Column.COLUMN_UNIQUE_ID_INIT_VALUE,
defaultValue.getValue());
}
@Override

View File

@ -90,6 +90,16 @@ public class Column implements Writable, GsonPostProcessable {
private ColumnStats stats; // cardinality and selectivity etc.
@SerializedName(value = "children")
private List<Column> children;
/**
* This is similar as `defaultValue`. Differences are:
* 1. `realDefaultValue` indicates the **default underlying literal**.
* 2. Instead, `defaultValue` indicates the **original expression** which is specified by users.
*
* For example, if user create a table with (columnA, DATETIME, DEFAULT CURRENT_TIMESTAMP)
* `realDefaultValue` here is current date time while `defaultValue` is `CURRENT_TIMESTAMP`.
*/
@SerializedName(value = "realDefaultValue")
private String realDefaultValue;
// Define expr may exist in two forms, one is analyzed, and the other is not analyzed.
// Currently, analyzed define expr is only used when creating materialized views,
// so the define expr in RollupJob must be analyzed.
@ -137,12 +147,18 @@ public class Column implements Writable, GsonPostProcessable {
public Column(String name, Type type, boolean isKey, AggregateType aggregateType, boolean isAllowNull,
String defaultValue, String comment) {
this(name, type, isKey, aggregateType, isAllowNull, defaultValue, comment, true, null,
COLUMN_UNIQUE_ID_INIT_VALUE);
COLUMN_UNIQUE_ID_INIT_VALUE, defaultValue);
}
public Column(String name, Type type, boolean isKey, AggregateType aggregateType, boolean isAllowNull,
String comment, boolean visible, int colUniqueId) {
this(name, type, isKey, aggregateType, isAllowNull, null, comment, visible, null,
colUniqueId, null);
}
public Column(String name, Type type, boolean isKey, AggregateType aggregateType, boolean isAllowNull,
String defaultValue, String comment, boolean visible, DefaultValueExprDef defaultValueExprDef,
int colUniqueId) {
int colUniqueId, String realDefaultValue) {
this.name = name;
if (this.name == null) {
this.name = "";
@ -158,6 +174,7 @@ public class Column implements Writable, GsonPostProcessable {
this.isKey = isKey;
this.isAllowNull = isAllowNull;
this.defaultValue = defaultValue;
this.realDefaultValue = realDefaultValue;
this.defaultValueExprDef = defaultValueExprDef;
this.comment = comment;
this.stats = new ColumnStats();
@ -176,6 +193,7 @@ public class Column implements Writable, GsonPostProcessable {
this.isCompoundKey = column.isCompoundKey();
this.isAllowNull = column.isAllowNull();
this.defaultValue = column.getDefaultValue();
this.realDefaultValue = column.realDefaultValue;
this.defaultValueExprDef = column.defaultValueExprDef;
this.comment = column.getComment();
this.stats = column.getStats();
@ -393,7 +411,8 @@ public class Column implements Writable, GsonPostProcessable {
}
tColumn.setIsKey(this.isKey);
tColumn.setIsAllowNull(this.isAllowNull);
tColumn.setDefaultValue(this.defaultValue);
// keep compatibility
tColumn.setDefaultValue(this.realDefaultValue == null ? this.defaultValue : this.realDefaultValue);
tColumn.setVisible(visible);
toChildrenThrift(this, tColumn);
@ -591,7 +610,8 @@ public class Column implements Writable, GsonPostProcessable {
@Override
public int hashCode() {
return Objects.hash(name, getDataType(), getStrLen(), getPrecision(), getScale(), aggregationType,
isAggregationTypeImplicit, isKey, isAllowNull, defaultValue, comment, children, visible);
isAggregationTypeImplicit, isKey, isAllowNull, defaultValue, comment, children, visible,
realDefaultValue);
}
@Override
@ -617,7 +637,8 @@ public class Column implements Writable, GsonPostProcessable {
&& getScale() == other.getScale()
&& Objects.equals(comment, other.comment)
&& visible == other.visible
&& Objects.equals(children, other.children);
&& Objects.equals(children, other.children)
&& Objects.equals(realDefaultValue, other.realDefaultValue);
}
@Override
@ -640,6 +661,7 @@ public class Column implements Writable, GsonPostProcessable {
notNull = in.readBoolean();
if (notNull) {
defaultValue = Text.readString(in);
realDefaultValue = defaultValue;
}
stats = ColumnStats.read(in);

View File

@ -192,7 +192,7 @@ public class HMSExternalCatalog extends ExternalCatalog {
for (FieldSchema field : schema) {
tmpSchema.add(new Column(field.getName(),
HiveMetaStoreClientHelper.hiveTypeToDorisType(field.getType()), true, null,
true, null, field.getComment(), true, null, -1));
true, field.getComment(), true, -1));
}
return tmpSchema;
}
@ -204,7 +204,7 @@ public class HMSExternalCatalog extends ExternalCatalog {
for (FieldSchema field : hmsSchema) {
tmpSchema.add(new Column(field.getName(),
HiveMetaStoreClientHelper.hiveTypeToDorisType(field.getType()), true, null,
true, null, field.getComment(), true, null,
true, field.getComment(), true,
schema.caseInsensitiveFindField(field.getName()).fieldId()));
}
return tmpSchema;

View File

@ -185,7 +185,7 @@ public abstract class IcebergExternalCatalog extends ExternalCatalog {
for (Types.NestedField field : columns) {
tmpSchema.add(new Column(field.name(),
icebergTypeToDorisType(field.type()), true, null,
true, null, field.doc(), true, null, -1));
true, field.doc(), true, -1));
}
return tmpSchema;
}

View File

@ -678,8 +678,8 @@ public class JdbcClient {
for (JdbcFieldSchema field : jdbcTableSchema) {
dorisTableSchema.add(new Column(field.getColumnName(),
jdbcTypeToDoris(field), true, null,
true, null, field.getRemarks(),
true, null, -1));
true, field.getRemarks(),
true, -1));
}
return dorisTableSchema;
}

View File

@ -0,0 +1,4 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !sql --
2

View File

@ -0,0 +1,54 @@
// 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_default_current_timestamp") {
def tbName = "test_default_current_timestamp"
sql "DROP TABLE IF EXISTS ${tbName}"
sql """
CREATE TABLE IF NOT EXISTS ${tbName} (
`house_id` bigint(20) NULL DEFAULT "-1" COMMENT '仓库id',
`pick_order_big_num` decimal(27, 9) NULL
)
UNIQUE KEY(house_id)
DISTRIBUTED BY HASH(house_id) BUCKETS 5 properties("replication_num" = "1");
"""
sql "insert into ${tbName} values(1,1.1)"
sql "insert into ${tbName} values(2,1.1)"
sql """ ALTER TABLE ${tbName} ADD COLUMN compute_time datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '计算时间' AFTER pick_order_big_num; """
def getJobState = { tableName ->
def jobStateResult = sql """ SHOW ALTER TABLE COLUMN WHERE IndexName='${tableName}' ORDER BY createtime DESC LIMIT 1 """
return jobStateResult[0][9]
}
int max_try_time = 1000
while(max_try_time--){
String result = getJobState(tbName)
if (result == "FINISHED") {
break
} else {
sleep(100)
if (max_try_time < 1){
assertEquals(1,2)
}
}
}
sql """sync"""
qt_sql """ SELECT COUNT(*) FROM ${tbName} WHERE date(compute_time) = curdate() """
sql "DROP TABLE ${tbName}"
}