[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:
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
-- This file is automatically generated. You should know what you did if you want to edit this
|
||||
-- !sql --
|
||||
2
|
||||
|
||||
@ -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}"
|
||||
}
|
||||
Reference in New Issue
Block a user