lancelly commented on code in PR #9438: URL: https://github.com/apache/iotdb/pull/9438#discussion_r1155248386
########## docs/UserGuide/Operators-Functions/Conditional.md: ########## @@ -0,0 +1,151 @@ +<!-- + + 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. + +--> + +## Conditional Expressions + +### CASE + +#### Introduction + +The CASE expression is similar to the if/else statement in other languages. + +The syntax is as follows: + +- Format 1: + ```sql + CASE + WHEN condition1 THEN expression1 + [WHEN condition2 THEN expression2] ... + [ELSE expression_end] + END + ``` + The `condition`s are evaluated one by one. +- The first `condition` that is true will return the corresponding `expression`. +- 格式2: Review Comment: Formart 2: ########## docs/UserGuide/Operators-Functions/Conditional.md: ########## @@ -0,0 +1,151 @@ +<!-- + + 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. + +--> + +## Conditional Expressions + +### CASE + +#### Introduction + +The CASE expression is similar to the if/else statement in other languages. + +The syntax is as follows: + +- Format 1: + ```sql + CASE + WHEN condition1 THEN expression1 + [WHEN condition2 THEN expression2] ... + [ELSE expression_end] + END + ``` + The `condition`s are evaluated one by one. +- The first `condition` that is true will return the corresponding `expression`. +- 格式2: + ```sql + CASE caseValue + WHEN whenValue1 THEN expression1 + [WHEN whenValue2 THEN expression2] ... + [ELSE expression_end] + END + ``` + The `caseValue` is evaluated first, and then the `whenValue`s are evaluated one by one. The first `whenValue` that is equal to the `caseValue` will return the corresponding `expression`. + + Format 2 will be transformed into an equivalent Format 1 by iotdb. + For example, the above SQL statement will be transformed into: + ```sql + CASE + WHEN caseValue=whenValue1 THEN expression1 + [WHEN caseValue=whenValue1 THEN expression1] ... + [ELSE expression_end] + END + ``` + +如果condition均不为真,或均不满足caseVaule=whenValue,则返回expression_end;不存在ELSE子句则返回null。 Review Comment: translate it ########## antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4: ########## @@ -975,6 +976,23 @@ expression | leftExpression=expression OPERATOR_OR rightExpression=expression ; +caseWhenThenExpression + : caseWhenThenExpression1 + | caseWhenThenExpression2 + ; + +caseWhenThenExpression1 + : CASE whenThenExpression+ (ELSE elseExpression=expression)? END + ; + +caseWhenThenExpression2 + : CASE caseExpression=expression whenThenExpression+ (ELSE elseExpression=expression)? END + ; Review Comment: ```suggestion caseWhenThenExpression : CASE caseExpression=expression? whenThenExpression+ (ELSE elseExpression=expression)? END ; ``` ########## docs/UserGuide/Operators-Functions/Conditional.md: ########## @@ -0,0 +1,151 @@ +<!-- + + 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. + +--> + +## Conditional Expressions + +### CASE + +#### Introduction + +The CASE expression is similar to the if/else statement in other languages. + +The syntax is as follows: + +- Format 1: + ```sql + CASE + WHEN condition1 THEN expression1 + [WHEN condition2 THEN expression2] ... + [ELSE expression_end] + END + ``` + The `condition`s are evaluated one by one. +- The first `condition` that is true will return the corresponding `expression`. +- 格式2: + ```sql + CASE caseValue + WHEN whenValue1 THEN expression1 + [WHEN whenValue2 THEN expression2] ... + [ELSE expression_end] + END + ``` + The `caseValue` is evaluated first, and then the `whenValue`s are evaluated one by one. The first `whenValue` that is equal to the `caseValue` will return the corresponding `expression`. + + Format 2 will be transformed into an equivalent Format 1 by iotdb. + For example, the above SQL statement will be transformed into: + ```sql + CASE + WHEN caseValue=whenValue1 THEN expression1 + [WHEN caseValue=whenValue1 THEN expression1] ... + [ELSE expression_end] + END + ``` + +如果condition均不为真,或均不满足caseVaule=whenValue,则返回expression_end;不存在ELSE子句则返回null。 + +If none of the conditions are true, or if none of the `whenValue`s match the `caseValue`, the `expression_end` will be returned. Review Comment: If none of the conditions is true, or if none of the `whenValue`s matches ########## docs/zh/UserGuide/Operators-Functions/Conditional.md: ########## @@ -0,0 +1,145 @@ +<!-- + + 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. + +--> + +## 条件表达式 + +### CASE + +#### 介绍 Review Comment: Please add the constraints as we discussed in test cases of this function in user doc. ########## docs/zh/UserGuide/Operators-Functions/Overview.md: ########## @@ -239,4 +239,12 @@ OR, |, || 1. 下载包含全部依赖的 jar 包和注册脚本 [【点击下载】](https://archive.apache.org/dist/iotdb/0.14.0-preview3/apache-iotdb-0.14.0-preview3-library-udf-bin.zip) ; 2. 将 jar 包复制到 IoTDB 程序目录的 `ext\udf` 目录下 (若您使用的是集群,请将jar包复制到所有DataNode的该目录下); 3. 启动 IoTDB; -4. 将注册脚本复制到 IoTDB 的程序目录下(与`sbin`目录同级的根目录下),修改脚本中的参数(如果需要)并运行注册脚本以注册 UDF。 \ No newline at end of file +4. 将注册脚本复制到 IoTDB 的程序目录下(与`sbin`目录同级的根目录下),修改脚本中的参数(如果需要)并运行注册脚本以注册 UDF。 + +## 条件表达式 + +| 表达式名称 | 含义 | +|---------------------------|-----------| +| `CASE` | 类似if else | + +详细说明及示例见文档 [条件表达式](./Conditional.md) Review Comment: It seems that this is in the wrong place. ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/other/CaseWhenThenExpression.java: ########## @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.other; + +import org.apache.iotdb.db.mpp.common.NodeRef; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.WhenThenExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.NullOperand; +import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CaseWhenThenExpression extends Expression { + protected List<WhenThenExpression> whenThenExpressions = new ArrayList<>(); + protected Expression elseExpression; + + public CaseWhenThenExpression( + List<WhenThenExpression> whenThenExpressions, Expression elseExpression) { + this.whenThenExpressions = whenThenExpressions; + this.elseExpression = elseExpression; + } + + public CaseWhenThenExpression(ByteBuffer byteBuffer) { + while (true) { + Expression expression = Expression.deserialize(byteBuffer); + if (expression.getExpressionType() == ExpressionType.WHEN_THEN) { + this.whenThenExpressions.add((WhenThenExpression) expression); + } else { + this.elseExpression = expression; + break; + } + } + } Review Comment: Add UT for serialize/deserialize ########## integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBCaseWhenThenIT.java: ########## @@ -0,0 +1,426 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.it.query; + +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.ClusterIT; +import org.apache.iotdb.itbase.category.LocalStandaloneIT; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +import static org.apache.iotdb.db.it.utils.TestUtils.assertTestFail; +import static org.apache.iotdb.db.it.utils.TestUtils.prepareData; +import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest; +import static org.apache.iotdb.itbase.constant.TestConstant.DEVICE; +import static org.apache.iotdb.itbase.constant.TestConstant.TIMESTAMP_STR; + +@RunWith(IoTDBTestRunner.class) +@Category({LocalStandaloneIT.class, ClusterIT.class}) +public class IoTDBCaseWhenThenIT { Review Comment: I have not examined the programming of this IT; kindly ensure it addresses the scenarios delineated in the test document. ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/other/CaseWhenThenExpression.java: ########## @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.other; + +import org.apache.iotdb.db.mpp.common.NodeRef; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.WhenThenExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.NullOperand; +import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CaseWhenThenExpression extends Expression { + protected List<WhenThenExpression> whenThenExpressions = new ArrayList<>(); + protected Expression elseExpression; + + public CaseWhenThenExpression( + List<WhenThenExpression> whenThenExpressions, Expression elseExpression) { + this.whenThenExpressions = whenThenExpressions; + this.elseExpression = elseExpression; + } + + public CaseWhenThenExpression(ByteBuffer byteBuffer) { + while (true) { + Expression expression = Expression.deserialize(byteBuffer); + if (expression.getExpressionType() == ExpressionType.WHEN_THEN) { + this.whenThenExpressions.add((WhenThenExpression) expression); + } else { + this.elseExpression = expression; + break; + } + } + } + + public void setElseExpression(Expression expression) { + this.elseExpression = expression; + } + + public List<WhenThenExpression> getWhenThenExpressions() { + return whenThenExpressions; + } + + public Expression getElseExpression() { + return elseExpression; + } + + @Override + public ExpressionType getExpressionType() { + return ExpressionType.CASE_WHEN_THEN; + } + + @Override + public boolean isMappable(Map<NodeRef<Expression>, TSDataType> expressionTypes) { + for (Expression expression : this.getExpressions()) { + if (!expression.isMappable(expressionTypes)) return false; Review Comment: ```suggestion if (!expression.isMappable(expressionTypes)) {return false;} ``` ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/other/CaseWhenThenExpression.java: ########## @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.other; + +import org.apache.iotdb.db.mpp.common.NodeRef; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.WhenThenExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.NullOperand; +import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CaseWhenThenExpression extends Expression { + protected List<WhenThenExpression> whenThenExpressions = new ArrayList<>(); + protected Expression elseExpression; + + public CaseWhenThenExpression( + List<WhenThenExpression> whenThenExpressions, Expression elseExpression) { + this.whenThenExpressions = whenThenExpressions; + this.elseExpression = elseExpression; + } + + public CaseWhenThenExpression(ByteBuffer byteBuffer) { + while (true) { + Expression expression = Expression.deserialize(byteBuffer); + if (expression.getExpressionType() == ExpressionType.WHEN_THEN) { + this.whenThenExpressions.add((WhenThenExpression) expression); + } else { + this.elseExpression = expression; + break; + } + } + } + + public void setElseExpression(Expression expression) { + this.elseExpression = expression; + } + + public List<WhenThenExpression> getWhenThenExpressions() { + return whenThenExpressions; + } + + public Expression getElseExpression() { + return elseExpression; + } + + @Override + public ExpressionType getExpressionType() { + return ExpressionType.CASE_WHEN_THEN; + } + + @Override + public boolean isMappable(Map<NodeRef<Expression>, TSDataType> expressionTypes) { + for (Expression expression : this.getExpressions()) { + if (!expression.isMappable(expressionTypes)) return false; + } + return true; + } + + @Override + protected boolean isConstantOperandInternal() { + for (Expression expression : this.getExpressions()) { + if (!expression.isConstantOperand()) return false; + } + return true; + } + + @Override + public void constructUdfExecutors( + Map<String, UDTFExecutor> expressionName2Executor, ZoneId zoneId) { + for (Expression expression : this.getExpressions()) { + expression.constructUdfExecutors(expressionName2Executor, zoneId); + } + } + + @Override + public void bindInputLayerColumnIndexWithExpression( + Map<String, List<InputLocation>> inputLocations) { + this.getExpressions() + .forEach(expression -> expression.bindInputLayerColumnIndexWithExpression(inputLocations)); + final String digest = toString(); + + if (inputLocations.containsKey(digest)) { + inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex(); + } + } + + @Override + public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) { + this.getExpressions() + .forEach(expression -> expression.updateStatisticsForMemoryAssigner(memoryAssigner)); + memoryAssigner.increaseExpressionReference(this); + } + + @Override + protected String getExpressionStringInternal() { + StringBuilder builder = new StringBuilder(); + builder.append("CASE "); + for (Expression expression : this.whenThenExpressions) { + builder.append(expression.toString()).append(" "); + } + if (!(this.elseExpression instanceof NullOperand)) { + builder.append("ELSE ").append(this.elseExpression.toString()).append(" "); + } + builder.append("END"); + return builder.toString(); + } + + @Override + protected void serialize(ByteBuffer byteBuffer) { + getExpressions().forEach(child -> Expression.serialize(child, byteBuffer)); + } + + @Override + protected void serialize(DataOutputStream stream) throws IOException { + for (Expression expression : this.getExpressions()) { + Expression.serialize(expression, stream); + } + } + + @Override + public List<Expression> getExpressions() { + List<Expression> result = new ArrayList<>(whenThenExpressions); + result.add(elseExpression); + return result; + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitCaseWhenThenExpression(this, context); + } + + // TODO: constructUdfExecutors Review Comment: delete this ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java: ########## @@ -214,9 +216,40 @@ public static ResultColumn.ColumnType identifyOutputColumnType( } return checkedType; } + } else if (expression instanceof CaseWhenThenExpression) { + // first, get all subexpression's type + CaseWhenThenExpression caseExpression = (CaseWhenThenExpression) expression; + List<ResultColumn.ColumnType> typeList = + caseExpression.getExpressions().stream() + .map(e -> identifyOutputColumnType(e, false)) + .collect(Collectors.toList()); + // if at least one subexpression is RAW, I'm RAW too + boolean rawFlag = + typeList.stream().anyMatch(columnType -> columnType == ResultColumn.ColumnType.RAW); + // if at least one subexpression is AGGREGATION, I'm AGGREGATION too + boolean aggregationFlag = + typeList.stream() + .anyMatch(columnType -> columnType == ResultColumn.ColumnType.AGGREGATION); + // not allow RAW && AGGREGATION + if (rawFlag && aggregationFlag) { + throw new SemanticException( + "Raw data and aggregation result hybrid calculation is not supported."); + } + // not allow all const + boolean allConst = + typeList.stream().allMatch(columnType -> columnType == ResultColumn.ColumnType.CONSTANT); + if (allConst) { + throw new SemanticException("Constant column is not supported."); + } + for (ResultColumn.ColumnType type : typeList) { + if (type != ResultColumn.ColumnType.CONSTANT) { + return type; + } + } + throw new IllegalArgumentException("shouldn't attach here"); Review Comment: IllegalStateException ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java: ########## @@ -2430,6 +2437,58 @@ private Expression parseRoundFunction( return functionExpression; } + private CaseWhenThenExpression parseCaseWhenThenExpression( + IoTDBSqlParser.CaseWhenThenExpressionContext context, boolean canUseFullPath) { + if (context.caseWhenThenExpression1() != null) { + return parseCaseWhenThenExpression1(context.caseWhenThenExpression1(), canUseFullPath); + } + + if (context.caseWhenThenExpression2() != null) { + return parseCaseWhenThenExpression2(context.caseWhenThenExpression2(), canUseFullPath); + } + + throw new UnsupportedOperationException(); + } + + private CaseWhenThenExpression parseCaseWhenThenExpression1( + IoTDBSqlParser.CaseWhenThenExpression1Context context, boolean canUseFullPath) { + List<WhenThenExpression> whenThenList = new ArrayList<>(); + for (IoTDBSqlParser.WhenThenExpressionContext whenThenExpressionContext : + context.whenThenExpression()) { + whenThenList.add(parseWhenThenExpression(whenThenExpressionContext, canUseFullPath)); + } + Expression elseExpression = new NullOperand(); + if (context.elseExpression != null) { + elseExpression = parseExpression(context.elseExpression, canUseFullPath); + } + return new CaseWhenThenExpression(whenThenList, elseExpression); + } + + private CaseWhenThenExpression parseCaseWhenThenExpression2( + IoTDBSqlParser.CaseWhenThenExpression2Context context, boolean canUseFullPath) { + Expression caseExpression = parseExpression(context.caseExpression, canUseFullPath); + List<WhenThenExpression> whenThenList = new ArrayList<>(); + for (IoTDBSqlParser.WhenThenExpressionContext whenThenExpressionContext : + context.whenThenExpression()) { + Expression when = parseExpression(whenThenExpressionContext.whenExpression, canUseFullPath); + Expression then = parseExpression(whenThenExpressionContext.thenExpression, canUseFullPath); + Expression comparison = new EqualToExpression(caseExpression, when); + whenThenList.add(new WhenThenExpression(comparison, then)); + } + Expression elseExpression = new NullOperand(); + if (context.elseExpression != null) { + elseExpression = parseExpression(context.elseExpression, canUseFullPath); + } + return new CaseWhenThenExpression(whenThenList, elseExpression); + } Review Comment: Refatcor these methods since IotdbSqlParser.g4 changes. ########## server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/column/CaseWhenThenColumnTransformer.java: ########## @@ -0,0 +1,131 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.transformation.dag.column; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder; +import org.apache.iotdb.tsfile.read.common.type.BinaryType; +import org.apache.iotdb.tsfile.read.common.type.BooleanType; +import org.apache.iotdb.tsfile.read.common.type.DoubleType; +import org.apache.iotdb.tsfile.read.common.type.FloatType; +import org.apache.iotdb.tsfile.read.common.type.IntType; +import org.apache.iotdb.tsfile.read.common.type.LongType; +import org.apache.iotdb.tsfile.read.common.type.Type; +import org.apache.iotdb.tsfile.utils.Pair; + +import java.util.ArrayList; +import java.util.List; + +public class CaseWhenThenColumnTransformer extends ColumnTransformer { + + // List<WhenThenColumnTransformer> whenThenTransformers; + List<Pair<ColumnTransformer, ColumnTransformer>> whenThenTransformers; + ColumnTransformer elseTransformer; + + public CaseWhenThenColumnTransformer( + Type returnType, + List<ColumnTransformer> whenTransformers, + List<ColumnTransformer> thenTransformers, + ColumnTransformer elseTransformer) { + super(returnType); + if (whenTransformers.size() != thenTransformers.size()) { + throw new UnsupportedOperationException( + "the size of whenTransformers and thenTransformers need to be same."); + } Review Comment: use Validate.isTrue() instead ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } + // if TEXT exist, every branch need to be TEXT + if (typeSet.contains(TSDataType.TEXT)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.TEXT)) { + throw new SemanticException( + "CASE expression: TEXT and other types cannot exist at same time"); Review Comment: at the same time. ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } + // if TEXT exist, every branch need to be TEXT Review Comment: if TEXT exists, every branch needs to be TEXT ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } + // if TEXT exist, every branch need to be TEXT + if (typeSet.contains(TSDataType.TEXT)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.TEXT)) { + throw new SemanticException( + "CASE expression: TEXT and other types cannot exist at same time"); + } + return setExpressionType(caseWhenThenExpression, TSDataType.TEXT); + } + // if BOOLEAN exist, every branch need to be BOOLEAN Review Comment: same as above ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } + // if TEXT exist, every branch need to be TEXT + if (typeSet.contains(TSDataType.TEXT)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.TEXT)) { + throw new SemanticException( + "CASE expression: TEXT and other types cannot exist at same time"); + } + return setExpressionType(caseWhenThenExpression, TSDataType.TEXT); + } + // if BOOLEAN exist, every branch need to be BOOLEAN + if (typeSet.contains(TSDataType.BOOLEAN)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.BOOLEAN)) { + throw new SemanticException( + "CASE expression: BOOLEAN and other types cannot exist at same time"); + } + return setExpressionType(caseWhenThenExpression, TSDataType.BOOLEAN); + } + // other 4 TSDataType can exist at same time + // because they can transform by Type, finally treated as DOUBLE Review Comment: be transformed ########## server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/column/CaseWhenThenColumnTransformer.java: ########## @@ -0,0 +1,131 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.transformation.dag.column; + +import org.apache.iotdb.tsfile.read.common.block.column.Column; +import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder; +import org.apache.iotdb.tsfile.read.common.type.BinaryType; +import org.apache.iotdb.tsfile.read.common.type.BooleanType; +import org.apache.iotdb.tsfile.read.common.type.DoubleType; +import org.apache.iotdb.tsfile.read.common.type.FloatType; +import org.apache.iotdb.tsfile.read.common.type.IntType; +import org.apache.iotdb.tsfile.read.common.type.LongType; +import org.apache.iotdb.tsfile.read.common.type.Type; +import org.apache.iotdb.tsfile.utils.Pair; + +import java.util.ArrayList; +import java.util.List; + +public class CaseWhenThenColumnTransformer extends ColumnTransformer { + + // List<WhenThenColumnTransformer> whenThenTransformers; + List<Pair<ColumnTransformer, ColumnTransformer>> whenThenTransformers; + ColumnTransformer elseTransformer; + + public CaseWhenThenColumnTransformer( + Type returnType, + List<ColumnTransformer> whenTransformers, + List<ColumnTransformer> thenTransformers, + ColumnTransformer elseTransformer) { + super(returnType); + if (whenTransformers.size() != thenTransformers.size()) { + throw new UnsupportedOperationException( + "the size of whenTransformers and thenTransformers need to be same."); + } + this.whenThenTransformers = new ArrayList<>(); + for (int i = 0; i < whenTransformers.size(); i++) { + this.whenThenTransformers.add(new Pair<>(whenTransformers.get(i), thenTransformers.get(i))); + } + this.elseTransformer = elseTransformer; + } + + public List<Pair<ColumnTransformer, ColumnTransformer>> getWhenThenColumnTransformers() { + return whenThenTransformers; + } + + public ColumnTransformer getElseTransformer() { + return elseTransformer; + } + + private void writeToColumnBuilder( + ColumnTransformer childTransformer, Column column, int index, ColumnBuilder builder) { + if (returnType instanceof BooleanType) { + builder.writeBoolean(childTransformer.getType().getBoolean(column, index)); + } else if (returnType instanceof IntType) { + builder.writeInt(childTransformer.getType().getInt(column, index)); + } else if (returnType instanceof LongType) { + builder.writeLong(childTransformer.getType().getLong(column, index)); + } else if (returnType instanceof FloatType) { + builder.writeFloat(childTransformer.getType().getFloat(column, index)); + } else if (returnType instanceof DoubleType) { + builder.writeDouble(childTransformer.getType().getDouble(column, index)); + } else if (returnType instanceof BinaryType) { + builder.writeBinary(childTransformer.getType().getBinary(column, index)); + } else { + throw new UnsupportedOperationException("Unsupported Type"); + } + } Review Comment: seems not necessary ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } + // if TEXT exist, every branch need to be TEXT + if (typeSet.contains(TSDataType.TEXT)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.TEXT)) { + throw new SemanticException( + "CASE expression: TEXT and other types cannot exist at same time"); + } + return setExpressionType(caseWhenThenExpression, TSDataType.TEXT); + } + // if BOOLEAN exist, every branch need to be BOOLEAN + if (typeSet.contains(TSDataType.BOOLEAN)) { + if (typeSet.stream().anyMatch(tsDataType -> tsDataType != TSDataType.BOOLEAN)) { + throw new SemanticException( + "CASE expression: BOOLEAN and other types cannot exist at same time"); Review Comment: same as above ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/visitor/IsDeviceViewNeedSpecialProcessVisitor.java: ########## @@ -0,0 +1,22 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.visitor; + +public class IsDeviceViewNeedSpecialProcessVisitor extends ReconstructVisitor<Boolean> {} Review Comment: Delete this? ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/visitor/ExpressionAnalyzeVisitor.java: ########## @@ -32,8 +32,10 @@ public R visitExpression(Expression expression, C context) { } List<R> getResultsFromChild(Expression expression, C context) { - return expression.getExpressions().stream() - .map(child -> process(child, context)) - .collect(Collectors.toList()); + List<R> result = new ArrayList<>(); + for (Expression child : expression.getExpressions()) { + result.add(this.process(child, context)); + } + return result; Review Comment: Perhaps we do not need this change? ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionTypeAnalyzer.java: ########## @@ -296,6 +300,52 @@ public TSDataType visitNullOperand(NullOperand nullOperand, Void context) { return null; } + @Override + public TSDataType visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, Void context) { + Set<TSDataType> typeSet = new HashSet<>(); + for (WhenThenExpression whenThenExpression : + caseWhenThenExpression.getWhenThenExpressions()) { + typeSet.add(process(whenThenExpression, context)); + } + TSDataType elseType = process(caseWhenThenExpression.getElseExpression(), context); + if (elseType != null) { + typeSet.add(elseType); + } Review Comment: ```suggestion if(caseWhenThenExpression.getElseExpression()!=null){ typeSet.add(process(caseWhenThenExpression.getElseExpression(), context)); } ``` ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/other/CaseWhenThenExpression.java: ########## @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.other; + +import org.apache.iotdb.db.mpp.common.NodeRef; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.WhenThenExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.NullOperand; +import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CaseWhenThenExpression extends Expression { + protected List<WhenThenExpression> whenThenExpressions = new ArrayList<>(); + protected Expression elseExpression; + + public CaseWhenThenExpression( + List<WhenThenExpression> whenThenExpressions, Expression elseExpression) { + this.whenThenExpressions = whenThenExpressions; + this.elseExpression = elseExpression; + } + + public CaseWhenThenExpression(ByteBuffer byteBuffer) { + while (true) { + Expression expression = Expression.deserialize(byteBuffer); + if (expression.getExpressionType() == ExpressionType.WHEN_THEN) { + this.whenThenExpressions.add((WhenThenExpression) expression); + } else { + this.elseExpression = expression; + break; + } + } + } + + public void setElseExpression(Expression expression) { + this.elseExpression = expression; + } + + public List<WhenThenExpression> getWhenThenExpressions() { + return whenThenExpressions; + } + + public Expression getElseExpression() { + return elseExpression; + } + + @Override + public ExpressionType getExpressionType() { + return ExpressionType.CASE_WHEN_THEN; + } + + @Override + public boolean isMappable(Map<NodeRef<Expression>, TSDataType> expressionTypes) { + for (Expression expression : this.getExpressions()) { + if (!expression.isMappable(expressionTypes)) return false; + } + return true; + } + + @Override + protected boolean isConstantOperandInternal() { + for (Expression expression : this.getExpressions()) { + if (!expression.isConstantOperand()) return false; Review Comment: same as above. ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/visitor/ColumnTransformerVisitor.java: ########## @@ -337,6 +341,47 @@ public ColumnTransformer visitNullOperand( return res; } + @Override + public ColumnTransformer visitCaseWhenThenExpression( + CaseWhenThenExpression caseWhenThenExpression, ColumnTransformerVisitorContext context) { + if (!context.cache.containsKey(caseWhenThenExpression)) { + if (context.hasSeen.containsKey(caseWhenThenExpression)) { + IdentityColumnTransformer identity = + new IdentityColumnTransformer( + TypeFactory.getType(context.getType(caseWhenThenExpression)), + context.originSize + context.commonTransformerList.size()); + ColumnTransformer columnTransformer = context.hasSeen.get(caseWhenThenExpression); + columnTransformer.addReferenceCount(); + context.commonTransformerList.add(columnTransformer); + context.leafList.add(identity); + context.inputDataTypes.add(context.getType(caseWhenThenExpression)); + context.cache.put(caseWhenThenExpression, identity); + } else { + // List<WhenThenColumnTransformer> whenThenColumnTransformers = new ArrayList<>(); Review Comment: delete this ########## server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/other/CaseWhenThenExpression.java: ########## @@ -0,0 +1,163 @@ +/* + * 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. + */ + +package org.apache.iotdb.db.mpp.plan.expression.other; + +import org.apache.iotdb.db.mpp.common.NodeRef; +import org.apache.iotdb.db.mpp.plan.expression.Expression; +import org.apache.iotdb.db.mpp.plan.expression.ExpressionType; +import org.apache.iotdb.db.mpp.plan.expression.binary.WhenThenExpression; +import org.apache.iotdb.db.mpp.plan.expression.leaf.NullOperand; +import org.apache.iotdb.db.mpp.plan.expression.visitor.ExpressionVisitor; +import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation; +import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner; +import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor; +import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CaseWhenThenExpression extends Expression { + protected List<WhenThenExpression> whenThenExpressions = new ArrayList<>(); + protected Expression elseExpression; + + public CaseWhenThenExpression( + List<WhenThenExpression> whenThenExpressions, Expression elseExpression) { + this.whenThenExpressions = whenThenExpressions; + this.elseExpression = elseExpression; + } + + public CaseWhenThenExpression(ByteBuffer byteBuffer) { + while (true) { + Expression expression = Expression.deserialize(byteBuffer); + if (expression.getExpressionType() == ExpressionType.WHEN_THEN) { + this.whenThenExpressions.add((WhenThenExpression) expression); + } else { + this.elseExpression = expression; + break; + } + } + } Review Comment: record the size of Arraylist instead of using while true -- 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: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
