morrySnow commented on code in PR #35284: URL: https://github.com/apache/doris/pull/35284#discussion_r1628900860
########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java: ########## @@ -344,6 +357,31 @@ public static Plan normalizePlan(Plan plan, TableIf table) { StatementScopeIdGenerator.newRelationId(), constantExprs.build())); } List<LogicalPlan> oneRowRelations = oneRowRelationBuilder.build(); + + // if (plan instanceof UnboundTableSink) { + // List<String> newColNames = new ArrayList<>(); + // if (unboundLogicalSink.getColNames().isEmpty()) { + // for (Column column : table.getBaseSchema(false)) { + // if (column.getGeneratedColumnInfo() != null) { + // continue; + // } + // newColNames.add(column.getName()); + // } + // } else { + // for (String name : unboundLogicalSink.getColNames()) { + // Column column = table.getColumn(name); + // if (column.getGeneratedColumnInfo() != null) { + // continue; + // } + // newColNames.add(name); + // } + // } + // if (!unboundLogicalSink.getColNames().isEmpty() && newColNames.isEmpty()) { + // + // } + // plan = ((UnboundTableSink) plan).withColNames(newColNames); + // } Review Comment: remove useless code ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/insert/InsertUtils.java: ########## @@ -383,6 +421,10 @@ public static TableIf getTargetTable(Plan plan, ConnectContext ctx) { private static NamedExpression generateDefaultExpression(Column column) { try { + GeneratedColumnInfo generatedColumnInfo = column.getGeneratedColumnInfo(); + if (generatedColumnInfo != null) { + return new Alias(new NullLiteral(DataType.fromCatalogType(column.getType())), column.getName()); Review Comment: add comment to explain why use nullliteral as a placeholder ########## regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy: ########## @@ -0,0 +1,159 @@ +// 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_generated_column_fault_tolerance_nereids") { + sql "SET enable_nereids_planner=true;" + sql "SET enable_fallback_to_original_planner=false;" + test { + sql """ + create table gencol_type_check(a int,b int, c array<int> generated always as (abs(a+b,3)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Can not found function 'abs' which has 2 arity." + } + + // gencol_has_sum + test { + sql """ + create table gencol_has_sum(a int,b int, c int generated always as (sum(a)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Expression of generated column 'c' contains a disallowed function" + } + + // gencol_has_column_not_define + test { + sql """ + create table gencol_has_sum(a int,b int, c int generated always as (abs(d)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Unknown column 'd' in 'generated column function'" + } + + // gencol_refer_gencol_after + test { + sql """ + create table gencol_refer_gencol(a int,c double generated always as (abs(a+d)) not null,b int, d int generated always as(c+1)) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Generated column can refer only to generated columns defined prior to it." + } + + sql "set @myvar=2" + // gencol_has_var + test { + sql """ + create table test_gen_col_not_null100(a varchar(10),c double generated always as (abs(a+b+@myvar)) not null,b int) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Generated column expression cannot contain variable." + } + + test { + sql """ + create table test_gen_col_auto_increment(a bigint not null auto_increment, b int, c int as (a*b)) + distributed by hash(a) properties("replication_num" = "1"); + """ + exception "Generated column 'c' cannot refer to auto-increment column." + } + + test{ + sql """ + create table test_gen_col_subquery(a int,b int, c int generated always as (a+(select 1)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Generated column does not support subquery." + } + + test { + sql """ + create table test_gen_col_array_func_lambda(pk int,a array<int>,b array<int>, c array<int> generated always as (array_count(x->(x%2=0),b)) not null) + DISTRIBUTED BY HASH(pk) + PROPERTIES("replication_num" = "1"); + """ + exception "Generated column does not support lambda." + } + + test { + sql """ + create table test_gen_col_array_func(pk int,a array<int>,b array<int>, c double generated always as (a+b) not null) + DISTRIBUTED BY HASH(pk) + PROPERTIES("replication_num" = "1"); + """ + exception "Cannot cast from ARRAY<INT> to numeric type" Review Comment: The error message should mention that it is related to a generated column ########## fe/fe-core/src/main/java/org/apache/doris/alter/SchemaChangeHandler.java: ########## @@ -376,6 +378,21 @@ private boolean processDropColumn(DropColumnClause alterClause, OlapTable olapTa } } + // generated column check + Map<String, Column> nameToColumn = new HashMap<>(); Review Comment: check alter table column datatype in next pr? ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java: ########## @@ -381,6 +387,20 @@ private static Map<String, NamedExpression> getColumnToOutput( } } } + for (Column column : generatedColumns) { Review Comment: add this explaination to a comment to avoid confusing when reading code ########## fe/fe-core/src/main/java/org/apache/doris/catalog/Column.java: ########## @@ -931,6 +947,9 @@ public String toSql(boolean isUniqueTable, boolean isCompatible) { } else { sb.append(typeStr); } + if (generatedColumnInfo != null) { + return generatedColumnToSql(sb); Review Comment: why not just append generated column info here and reuse other code? ########## fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundTableSink.java: ########## @@ -173,6 +173,11 @@ public Plan withGroupExprLogicalPropChildren(Optional<GroupExpression> groupExpr isPartialUpdate, dmlCommandType, groupExpression, logicalProperties, children.get(0)); } + public Plan withColNames(List<String> colNames) { + return new UnboundTableSink<>(nameParts, colNames, hints, temporaryPartition, partitions, autoDetectPartition, + isPartialUpdate, dmlCommandType, groupExpression, Optional.of(getLogicalProperties()), child()); + } + Review Comment: unused function remove it ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java: ########## @@ -798,5 +824,209 @@ public CreateTableStmt translateToLegacyStmt() { public void setIsExternal(boolean isExternal) { this.isExternal = isExternal; } + + private void genreatedColumnCommonCheck() { + boolean hasGeneratedCol = false; + for (ColumnDefinition column : columns) { + if (column.getGeneratedColumnDesc().isPresent()) { + hasGeneratedCol = true; + break; + } + } + if (hasGeneratedCol && !engineName.equalsIgnoreCase("olap")) { + throw new AnalysisException("Tables can only have generated columns if the olap engine is used"); + } + if (hasGeneratedCol && keysType == KeysType.AGG_KEYS) { + throw new AnalysisException("Generated Column cannot be used in the aggregate table"); + } + } + + private void generatedColumnCheck(ConnectContext ctx) { + genreatedColumnCommonCheck(); + LogicalEmptyRelation plan = new LogicalEmptyRelation( + ConnectContext.get().getStatementContext().getNextRelationId(), + new ArrayList<>()); + CascadesContext cascadesContext = CascadesContext.initContext(ctx.getStatementContext(), plan, + PhysicalProperties.ANY); + Map<String, Slot> columnToSlotReference = Maps.newHashMap(); + Map<String, ColumnDefinition> nameToColumnDefinition = Maps.newHashMap(); + Map<Slot, SlotRefAndIdx> translateMap = Maps.newHashMap(); + for (int i = 0; i < columns.size(); i++) { + ColumnDefinition column = columns.get(i); + Slot slot = new SlotReference(column.getName(), column.getType()); + columnToSlotReference.put(column.getName(), slot); + nameToColumnDefinition.put(column.getName(), column); + SlotRef slotRef = new SlotRef(null, column.getName()); + slotRef.setType(column.getType().toCatalogDataType()); + translateMap.put(slot, new SlotRefAndIdx(slotRef, i, column.getGeneratedColumnDesc().isPresent())); + } + ExpressionToExpr.initializeslotRefMap(translateMap); + PlanTranslatorContext planTranslatorContext = new PlanTranslatorContext(cascadesContext); + List<Slot> slots = Lists.newArrayList(columnToSlotReference.values()); + List<GeneratedColumnUtil.ExprAndname> exprAndnames = Lists.newArrayList(); + for (int i = 0; i < columns.size(); i++) { + ColumnDefinition column = columns.get(i); + Optional<GeneratedColumnDesc> info = column.getGeneratedColumnDesc(); + if (!info.isPresent()) { + continue; + } + Expression parsedExpression = info.get().getExpression(); + checkparsedExpressionInGeneratedColumn(parsedExpression); Review Comment: ```suggestion checkParsedExpressionInGeneratedColumn(parsedExpression); ``` ########## fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java: ########## @@ -686,6 +686,17 @@ public String toSqlImpl() { sb.append(((FunctionCallExpr) expr).fnName); sb.append(" "); sb.append(children.get(1).toSql()); + } else if (fnName.getFunction().equalsIgnoreCase("encryptkeyref")) { Review Comment: if rely on tosql of Expr, we should check all subclass of Expr to ensure it is right later ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java: ########## @@ -798,5 +824,209 @@ public CreateTableStmt translateToLegacyStmt() { public void setIsExternal(boolean isExternal) { this.isExternal = isExternal; } + + private void genreatedColumnCommonCheck() { + boolean hasGeneratedCol = false; + for (ColumnDefinition column : columns) { + if (column.getGeneratedColumnDesc().isPresent()) { + hasGeneratedCol = true; + break; + } + } + if (hasGeneratedCol && !engineName.equalsIgnoreCase("olap")) { + throw new AnalysisException("Tables can only have generated columns if the olap engine is used"); + } + if (hasGeneratedCol && keysType == KeysType.AGG_KEYS) { + throw new AnalysisException("Generated Column cannot be used in the aggregate table"); Review Comment: why? could be used in key column? ########## regression-test/suites/ddl_p0/test_create_table_generated_column/test_generated_column_nereids.groovy: ########## Review Comment: add case `insert into tbl select ...`. we should throw exception when try to insert data into generated column ########## regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy: ########## @@ -0,0 +1,159 @@ +// 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_generated_column_fault_tolerance_nereids") { + sql "SET enable_nereids_planner=true;" + sql "SET enable_fallback_to_original_planner=false;" + test { + sql """ + create table gencol_type_check(a int,b int, c array<int> generated always as (abs(a+b,3)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Can not found function 'abs' which has 2 arity." + } + + // gencol_has_sum + test { + sql """ + create table gencol_has_sum(a int,b int, c int generated always as (sum(a)) not null) + DISTRIBUTED BY HASH(a) + PROPERTIES("replication_num" = "1"); + """ + exception "Expression of generated column 'c' contains a disallowed function" Review Comment: print the disallowed function sql? ########## regression-test/suites/ddl_p0/test_create_table_generated_column/fault_tolerance_nereids.groovy: ########## Review Comment: should print column name in error message ########## fe/fe-core/src/main/java/org/apache/doris/common/proc/IndexSchemaProcNode.java: ########## @@ -65,6 +65,9 @@ public static ProcResult createResult(List<Column> schema, Set<String> bfColumns if (column.isAutoInc()) { extras.add("AUTO_INCREMENT"); } + if (column.getGeneratedColumnInfo() != null) { + extras.add("GENERATED"); Review Comment: mysql print: STORED GENERATED or VIRTUAL GENERATED -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org