This is an automated email from the ASF dual-hosted git repository. ericpai pushed a commit to branch bugfix/iotdb-4729 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit f0d1574795d846f7a48b2a30c2556287bc70f6ff Author: ericpai <[email protected]> AuthorDate: Mon Oct 24 16:02:35 2022 +0800 [IOTDB-4729] Fix column display name dismatched with SQL input --- docs/UserGuide/Query-Data/Aggregate-Query.md | 6 ++-- docs/zh/UserGuide/Query-Data/Aggregate-Query.md | 8 +++--- .../it/aggregation/IoTDBAggregationByLevelIT.java | 24 ++++++++++++++-- .../db/it/aggregation/IoTDBTagAggregationIT.java | 32 +++++++++++----------- .../db/it/udf/IoTDBUDTFBuiltinFunctionIT.java | 20 ++++++++++++++ .../db/mpp/plan/analyze/ExpressionAnalyzer.java | 4 +-- .../iotdb/db/mpp/plan/expression/Expression.java | 19 ++++++++++++- .../plan/expression/multi/FunctionExpression.java | 10 +++++-- .../db/mpp/plan/planner/LogicalPlanBuilder.java | 9 +++++- .../db/mpp/plan/planner/SubPlanTypeExtractor.java | 10 +++++++ .../plan/parameter/AggregationDescriptor.java | 2 +- 11 files changed, 111 insertions(+), 33 deletions(-) diff --git a/docs/UserGuide/Query-Data/Aggregate-Query.md b/docs/UserGuide/Query-Data/Aggregate-Query.md index b23d21ef38..83d828798d 100644 --- a/docs/UserGuide/Query-Data/Aggregate-Query.md +++ b/docs/UserGuide/Query-Data/Aggregate-Query.md @@ -416,7 +416,7 @@ Result: ``` +-----------------------------+-------------------------+ -| Time|COUNT(root.ln.*.*.status)| +| Time|count(root.ln.*.*.status)| +-----------------------------+-------------------------+ |2017-11-02T00:00:00.000+08:00| 1440| |2017-11-03T00:00:00.000+08:00| 1440| @@ -440,7 +440,7 @@ Result: ``` +-----------------------------+-------------------------+ -| Time|COUNT(root.ln.*.*.status)| +| Time|count(root.ln.*.*.status)| +-----------------------------+-------------------------+ |2017-11-01T00:00:00.000+08:00| 180| |2017-11-02T00:00:00.000+08:00| 180| @@ -528,7 +528,7 @@ insert into root.factory1.d9(time, temperature) values(3000, 52.1); If the user wants to know the average temperature of each workshop, he can query like this ```SQL -SELECT AVG(temperature) FROM root.factory1.** GROUP BY TAGS(city); +SELECT avg(temperature) FROM root.factory1.** GROUP BY TAGS(city); ``` The query will calculate the average of the temperatures of those timeseries which have the same tag value of the key `city`. diff --git a/docs/zh/UserGuide/Query-Data/Aggregate-Query.md b/docs/zh/UserGuide/Query-Data/Aggregate-Query.md index db44805857..f2c541e591 100644 --- a/docs/zh/UserGuide/Query-Data/Aggregate-Query.md +++ b/docs/zh/UserGuide/Query-Data/Aggregate-Query.md @@ -412,7 +412,7 @@ select count(status) from root.ln.wf01.wt01 group by ((2017-11-01T00:00:00, 2017 ``` +-----------------------------+-------------------------+ -| Time|COUNT(root.ln.*.*.status)| +| Time|count(root.ln.*.*.status)| +-----------------------------+-------------------------+ |2017-11-02T00:00:00.000+08:00| 1440| |2017-11-03T00:00:00.000+08:00| 1440| @@ -434,7 +434,7 @@ select count(status) from root.ln.wf01.wt01 group by ([2017-11-01 00:00:00, 2017 ``` +-----------------------------+-------------------------+ -| Time|COUNT(root.ln.*.*.status)| +| Time|count(root.ln.*.*.status)| +-----------------------------+-------------------------+ |2017-11-01T00:00:00.000+08:00| 180| |2017-11-02T00:00:00.000+08:00| 180| @@ -518,7 +518,7 @@ insert into root.factory1.d9(time, temperature) values(3000, 52.1); 用户想统计该工厂每个地区的设备的温度的平均值,可以使用如下查询语句 ```SQL -SELECT AVG(temperature) FROM root.factory1.** GROUP BY TAGS(city); +SELECT avg(temperature) FROM root.factory1.** GROUP BY TAGS(city); ``` 该查询会将具有同一个 `city` 标签值的时间序列的所有满足查询条件的点做平均值计算,计算结果如下 @@ -579,7 +579,7 @@ It costs 0.027s SQL 语句如下 ```SQL -SELECT AVG(temperature) FROM root.factory1.** GROUP BY ([1000, 10000), 5s), TAGS(city, workshop); +SELECT avg(temperature) FROM root.factory1.** GROUP BY ([1000, 10000), 5s), TAGS(city, workshop); ``` 查询结果如下 diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationByLevelIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationByLevelIT.java index b1d07581ff..ddf8388143 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationByLevelIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationByLevelIT.java @@ -33,6 +33,7 @@ import org.junit.runner.RunWith; import java.sql.Connection; import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Statement; import static org.apache.iotdb.itbase.constant.TestConstant.TIMESTAMP_STR; @@ -320,9 +321,9 @@ public class IoTDBAggregationByLevelIT { } /** - * [root.sg.d1.temperature, root.sg.d2.temperature] with level = 1 + * [root.sg1.d1.temperature, root.sg1.d2.temperature] with level = 1 * - * <p>Result is [root.sg.*.temperature] + * <p>Result is [root.sg1.*.temperature] */ @Test public void groupByLevelWithAliasTest() throws Exception { @@ -364,6 +365,25 @@ public class IoTDBAggregationByLevelIT { } } + @Test + public void groupByLevelWithMixedLetterCase() throws SQLException { + String[] retArray = new String[] {"5", "5", "5"}; + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + + int cnt = 0; + try (ResultSet resultSet = + statement.executeQuery( + "select cOuNt(temperature) from root.sg1.d1, root.sg1.d2 GROUP BY level=1")) { + while (resultSet.next()) { + String ans = resultSet.getString("cOuNt(root.sg1.*.temperature)"); + Assert.assertEquals(retArray[cnt], ans); + cnt++; + } + } + } + } + /** * [root.sg.d1.temperature, root.sg.d2.temperature] with level = 2 * diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java index e679657064..4f8f13e3b7 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTagAggregationIT.java @@ -102,8 +102,8 @@ public class IoTDBTagAggregationIT { "SELECT COUNT(t), AVG(t), MAX_TIME(t), MIN_TIME(t), MAX_VALUE(t), MIN_VALUE(t), EXTREME(t) FROM root.sg.** GROUP BY TAGS(k1)"; // Expected result set: // +----+--------+------------------+-----------+-----------+------------+------------+----------+ - // | k1|count(t)| - // avg(t)|max_time(t)|min_time(t)|max_value(t)|min_value(t)|extreme(t)| + // | k1|COUNT(t)| + // AVG(t)|MAX_TIME(t)|MIN_TIME(t)|MAX_VALUE(t)|MIN_VALUE(t)|EXTREME(t)| // +----+--------+------------------+-----------+-----------+------------+------------+----------+ // |k1v2| 4|3.1000000536441803| 10| 1| 5.4| 1.3| // 5.4| @@ -118,13 +118,13 @@ public class IoTDBTagAggregationIT { Assert.assertEquals(8, resultSet.getMetaData().getColumnCount()); Assert.assertTrue(resultSet.next()); Assert.assertEquals("k1v2", resultSet.getString("k1")); - Assert.assertEquals(4L, resultSet.getLong("count(t)")); - Assert.assertEquals(3.1D, resultSet.getDouble("avg(t)"), DELTA); - Assert.assertEquals(10L, resultSet.getLong("max_time(t)")); - Assert.assertEquals(1L, resultSet.getLong("min_time(t)")); - Assert.assertEquals(5.4F, resultSet.getFloat("max_value(t)"), DELTA); - Assert.assertEquals(1.3F, resultSet.getFloat("min_value(t)"), DELTA); - Assert.assertEquals(5.4F, resultSet.getFloat("extreme(t)"), DELTA); + Assert.assertEquals(4L, resultSet.getLong("COUNT(t)")); + Assert.assertEquals(3.1D, resultSet.getDouble("AVG(t)"), DELTA); + Assert.assertEquals(10L, resultSet.getLong("MAX_TIME(t)")); + Assert.assertEquals(1L, resultSet.getLong("MIN_TIME(t)")); + Assert.assertEquals(5.4F, resultSet.getFloat("MAX_VALUE(t)"), DELTA); + Assert.assertEquals(1.3F, resultSet.getFloat("MIN_VALUE(t)"), DELTA); + Assert.assertEquals(5.4F, resultSet.getFloat("EXTREME(t)"), DELTA); Assert.assertTrue(resultSet.next()); Assert.assertEquals("k1v1", resultSet.getString(1)); Assert.assertEquals(6L, resultSet.getLong(2)); @@ -154,7 +154,7 @@ public class IoTDBTagAggregationIT { @Test @Ignore public void testAggregateFunctionsWithNestedExpression() { - String query = "SELECT COUNT(t + 1), AVG(t + 1) FROM root.sg.** GROUP BY TAGS(k1)"; + String query = "SELECT count(t + 1), avg(t + 1) FROM root.sg.** GROUP BY TAGS(k1)"; // Expected result set: // +----+------------+------------------+ // | k1|count(t + 1)| avg(t + 1)| @@ -206,7 +206,7 @@ public class IoTDBTagAggregationIT { @Ignore // TODO: support having in later commits public void testAggregateFunctionsWithHaving() { String query = - "SELECT COUNT(t), AVG(t), MAX_TIME(t), MIN_TIME(t), MAX_VALUE(t), MIN_VALUE(t), EXTREME(t) FROM root.sg.** GROUP BY TAGS(k1) HAVING avg(t) > 3"; + "SELECT count(t), avg(t), max_time(t), min_time(t), max_value(t), min_value(t), extreme(t) FROM root.sg.** GROUP BY TAGS(k1) HAVING avg(t) > 3"; // Expected result set: // +----+--------+------------------+-----------+-----------+------------+------------+----------+ // | k1|count(t)| @@ -249,7 +249,7 @@ public class IoTDBTagAggregationIT { @Test public void testMultipleAggregationKeys() { - String query = "SELECT COUNT(t) FROM root.sg.** GROUP BY TAGS(k1, k2)"; + String query = "SELECT count(t) FROM root.sg.** GROUP BY TAGS(k1, k2)"; // Expected result set: // +----+----+--------+ // | k1| k2|count(t)| @@ -286,7 +286,7 @@ public class IoTDBTagAggregationIT { @Test public void testAlongWithTimeAggregation() { - String query = "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 10ms), TAGS(k1)"; + String query = "SELECT count(t) from root.sg.** GROUP BY ([0, 20), 10ms), TAGS(k1)"; // Expected result set: // +-----------------------------+----+--------+ // | Time| k1|count(t)| @@ -337,7 +337,7 @@ public class IoTDBTagAggregationIT { @Test public void testAlongWithSlidingWindow() { - String query = "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 15ms, 5ms), TAGS(k1)"; + String query = "SELECT count(t) from root.sg.** GROUP BY ([0, 20), 15ms, 5ms), TAGS(k1)"; // Expected result set: // +-----------------------------+----+--------+ // | Time| k1|count(t)| @@ -391,7 +391,7 @@ public class IoTDBTagAggregationIT { @Test public void testAlongWithTimeAggregationAndOrdering() { String query = - "SELECT COUNT(t) from root.sg.** GROUP BY ([0, 20), 10ms), TAGS(k1) ORDER BY TIME DESC"; + "SELECT count(t) from root.sg.** GROUP BY ([0, 20), 10ms), TAGS(k1) ORDER BY TIME DESC"; // Expected result set: // +-----------------------------+----+--------+ // | Time| k1|count(t)| @@ -442,7 +442,7 @@ public class IoTDBTagAggregationIT { @Test public void testAlongWithTimeFiltering() { - String query = "SELECT COUNT(t) FROM root.sg.** WHERE time > 1 GROUP BY TAGS(k1)"; + String query = "SELECT count(t) FROM root.sg.** WHERE time > 1 GROUP BY TAGS(k1)"; // Expected result set: // +----+--------+ // | k1|count(t)| diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/udf/IoTDBUDTFBuiltinFunctionIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/udf/IoTDBUDTFBuiltinFunctionIT.java index 8ef47e56f1..8f4cfc24db 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/udf/IoTDBUDTFBuiltinFunctionIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/udf/IoTDBUDTFBuiltinFunctionIT.java @@ -123,6 +123,26 @@ public class IoTDBUDTFBuiltinFunctionIT { double invoke(double x); } + @Test + public void testMixedLetterCaseFunctionName() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + try (ResultSet resultSet = statement.executeQuery("select SiN(s1) from root.sg.d1")) { + assertEquals(2, resultSet.getMetaData().getColumnCount()); + + for (int i = 0; i < INSERTION_SQLS.length; ++i) { + assertTrue(resultSet.next()); + double expected = Math.sin(i); + double actual = Double.parseDouble(resultSet.getString("SiN(root.sg.d1.s1)")); + assertEquals(expected, actual, E); + } + assertFalse(resultSet.next()); + } + } catch (SQLException throwable) { + fail(throwable.getMessage()); + } + } + private void testMathFunction(String functionName, MathFunctionProxy functionProxy) { try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java index 0f9e31dae6..e08e061177 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java @@ -1030,10 +1030,8 @@ public class ExpressionAnalyzer { for (Expression suffixExpression : expression.getExpressions()) { childExpressions.add(removeAliasFromExpression(suffixExpression)); } - // Reconstruct the function name to lower case to finish the calculation afterwards while the - // origin name will be only as output name return new FunctionExpression( - functionExpression.getFunctionName().toLowerCase(), + functionExpression.getFunctionName(), functionExpression.getFunctionAttributes(), childExpressions); } else if (expression instanceof TimeSeriesOperand) { diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java index 3847412f71..2d302a69fb 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java @@ -169,6 +169,7 @@ public abstract class Expression extends StatementNode { ///////////////////////////////////////////////////////////////////////////////////////////////// private String expressionStringCache; + private String expressionDisplayStringCache; /** Sub-classes must not override this method. */ @Override @@ -189,12 +190,28 @@ public abstract class Expression extends StatementNode { return expressionStringCache; } + /** Get the representation of the expression in string for the column headers. */ + public final String getExpressionDisplayString() { + if (expressionDisplayStringCache == null) { + expressionDisplayStringCache = getExpressionDisplayStringInternal(); + } + return expressionDisplayStringCache; + } + /** - * Sub-classes should override this method to provide valid string representation of this object. + * Subclasses should override this method to provide valid string representation of this object. * See {@link #getExpressionString()} */ protected abstract String getExpressionStringInternal(); + /** + * If a subclass wants to use a different display string, it can overwrite this method. Or use + * {@link #getExpressionString()} by default. + */ + protected String getExpressionDisplayStringInternal() { + return getExpressionStringInternal(); + } + ///////////////////////////////////////////////////////////////////////////////////////////////// // hashCode & equals ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java index 33694b6d95..525873c4d5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/multi/FunctionExpression.java @@ -38,6 +38,8 @@ import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils; import org.apache.iotdb.udf.api.customizer.strategy.AccessStrategy; +import org.apache.commons.lang3.Validate; + import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; @@ -106,7 +108,7 @@ public class FunctionExpression extends Expression { public FunctionExpression(ByteBuffer byteBuffer) { functionName = ReadWriteIOUtils.readString(byteBuffer); - + Validate.notNull(functionName); functionAttributes = ReadWriteIOUtils.readLinkedHashMap(byteBuffer); int expressionSize = ReadWriteIOUtils.readInt(byteBuffer); @@ -116,7 +118,7 @@ public class FunctionExpression extends Expression { } isBuiltInAggregationFunctionExpression = - BuiltinAggregationFunction.getNativeFunctionNames().contains(functionName); + BuiltinAggregationFunction.getNativeFunctionNames().contains(functionName.toLowerCase()); isConstantOperandCache = expressions.stream().anyMatch(Expression::isConstantOperand); isUserDefinedAggregationFunctionExpression = expressions.stream() @@ -307,6 +309,10 @@ public class FunctionExpression extends Expression { return functionName + "(" + getParametersString() + ")"; } + public String getProcessExpressionString() { + return functionName.toLowerCase() + "(" + getParametersString() + ")"; + } + /** * Generates the parameter part of the function column name. * diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java index 31643cd550..a67be8c7d4 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/LogicalPlanBuilder.java @@ -137,6 +137,13 @@ public class LogicalPlanBuilder { .getTypeProvider() .setType(expression.toString(), getPreAnalyzedType.apply(expression)); } + if (expression instanceof FunctionExpression) { + FunctionExpression funcExp = (FunctionExpression) expression; + context + .getTypeProvider() + .setType( + funcExp.getProcessExpressionString(), getPreAnalyzedType.apply(expression)); + } }); } @@ -665,7 +672,7 @@ public class LogicalPlanBuilder { } Expression next = iter.next(); if (next.equals(groupByTagOutputExpression)) { - String functionName = ((FunctionExpression) next).getFunctionName().toUpperCase(); + String functionName = ((FunctionExpression) next).getFunctionName().toLowerCase(); CrossSeriesAggregationDescriptor aggregationDescriptor = new CrossSeriesAggregationDescriptor( functionName, diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/SubPlanTypeExtractor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/SubPlanTypeExtractor.java index a95adc10c8..76628fbce5 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/SubPlanTypeExtractor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/SubPlanTypeExtractor.java @@ -102,6 +102,16 @@ public class SubPlanTypeExtractor { node.getTagValuesToAggregationDescriptors() .values() .forEach(this::updateTypeProviderByAggregationDescriptor); + node.getTagValuesToAggregationDescriptors() + .values() + .forEach( + v -> + v.forEach( + descriptor -> + descriptor + .getOutputColumnNames() + .forEach( + name -> typeProvider.setType(name, allTypes.getType(name))))); return visitPlan(node, context); } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java index 464ee03130..16f3896951 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java @@ -140,7 +140,7 @@ public class AggregationDescriptor { outputAggregationNames.add(aggregationFuncName.toLowerCase()); } } else { - outputAggregationNames.add(aggregationFuncName.toLowerCase()); + outputAggregationNames.add(aggregationFuncName); } return outputAggregationNames; }
