This is an automated email from the ASF dual-hosted git repository. hui pushed a commit to branch lmh/mppSelectInto in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit fb63d17c6b28c0a1e4d88fdd6fc21cb44b2d256c Author: Minghui Liu <[email protected]> AuthorDate: Mon Oct 10 22:26:47 2022 +0800 tmp save (analyzer for SELECT INTO) --- .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 6 +- .../db/metadata/path/PatternTreeMapFactory.java | 8 + .../apache/iotdb/db/mpp/plan/analyze/Analysis.java | 34 +-- .../iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java | 233 ++++++++++++++++++--- .../iotdb/db/mpp/plan/parser/ASTVisitor.java | 42 ++-- .../component/AlignByDeviceIntoComponent.java | 40 ---- .../component/AlignByTimeIntoComponent.java | 38 ---- .../plan/statement/component/IntoComponent.java | 25 ++- .../db/mpp/plan/statement/component/IntoItem.java | 73 +++++++ .../db/mpp/plan/statement/crud/QueryStatement.java | 10 - .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java | 14 +- 11 files changed, 354 insertions(+), 169 deletions(-) diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index d49d19b11e..591de9e067 100644 --- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -381,7 +381,7 @@ selectStatement intoClause : INTO ALIGNED? intoPath (COMMA intoPath)* - | INTO ALIGNED? intoDeviceAndMeasurement (COMMA intoDeviceAndMeasurement)* + | INTO intoItem (COMMA intoItem)* ; intoPath @@ -389,8 +389,8 @@ intoPath | nodeNameInIntoPath (DOT nodeNameInIntoPath)* #suffixPathInIntoPath ; -intoDeviceAndMeasurement - : intoPath LR_BRACKET nodeNameInIntoPath (COMMA nodeNameInIntoPath)* RR_BRACKET +intoItem + : ALIGNED? intoPath LR_BRACKET nodeNameInIntoPath (COMMA nodeNameInIntoPath)* RR_BRACKET ; nodeNameInIntoPath diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/path/PatternTreeMapFactory.java b/server/src/main/java/org/apache/iotdb/db/metadata/path/PatternTreeMapFactory.java index fe2e55869e..e1ce413315 100644 --- a/server/src/main/java/org/apache/iotdb/db/metadata/path/PatternTreeMapFactory.java +++ b/server/src/main/java/org/apache/iotdb/db/metadata/path/PatternTreeMapFactory.java @@ -49,6 +49,14 @@ public class PatternTreeMapFactory { HashSet::new, (mod, set) -> set.add(mod), null, ModsSerializer.getInstance()); } + public static PatternTreeMap<String, StringSerializer> getIntoPathPatternTreeMap() { + return new PatternTreeMap<>( + HashSet::new, + (columnName, set) -> set.add(columnName), + (columnName, set) -> set.remove(columnName), + StringSerializer.getInstance()); + } + public static class ModsSerializer implements PathPatternNode.Serializer<Modification> { @Override diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java index a5140a68e3..119f67654a 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/Analysis.java @@ -24,6 +24,8 @@ import org.apache.iotdb.common.rpc.thrift.TSchemaNode; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.path.PatternTreeMap; +import org.apache.iotdb.db.metadata.path.PatternTreeMapFactory; import org.apache.iotdb.db.metadata.template.Template; import org.apache.iotdb.db.mpp.common.NodeRef; import org.apache.iotdb.db.mpp.common.header.DatasetHeader; @@ -86,9 +88,6 @@ public class Analysis { // map from grouped path name to list of input aggregation in `GROUP BY LEVEL` clause private Map<Expression, Set<Expression>> groupByLevelExpressions; - // map from output column to target into path - private Map<String, PartialPath> outputColumnToIntoPathMap; - ///////////////////////////////////////////////////////////////////////////////////////////////// // Query Analysis (used in ALIGN BY DEVICE) ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -114,9 +113,6 @@ public class Analysis { private Set<Expression> deviceViewOutputExpressions; - // map from device name to target into path of each output column - private Map<String, Map<String, PartialPath>> deviceToIntoPathMap; - ///////////////////////////////////////////////////////////////////////////////////////////////// // Query Common Analysis (above DeviceView) ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -143,6 +139,14 @@ public class Analysis { // header of result dataset private DatasetHeader respDatasetHeader; + ///////////////////////////////////////////////////////////////////////////////////////////////// + // SELECT INTO Analysis (used in ALIGN BY DEVICE) + ///////////////////////////////////////////////////////////////////////////////////////////////// + + private PatternTreeMap<String, PatternTreeMapFactory.StringSerializer> intoPathPatternTreeMap; + + private Map<PartialPath, Boolean> intoDeviceToAlignedMap; + ///////////////////////////////////////////////////////////////////////////////////////////////// // Schema Query Analysis ///////////////////////////////////////////////////////////////////////////////////////////////// @@ -410,19 +414,21 @@ public class Analysis { this.deviceViewOutputExpressions = deviceViewOutputExpressions; } - public Map<String, PartialPath> getOutputColumnToIntoPathMap() { - return outputColumnToIntoPathMap; + public PatternTreeMap<String, PatternTreeMapFactory.StringSerializer> + getIntoPathPatternTreeMap() { + return intoPathPatternTreeMap; } - public void setOutputColumnToIntoPathMap(Map<String, PartialPath> outputColumnToIntoPathMap) { - this.outputColumnToIntoPathMap = outputColumnToIntoPathMap; + public void setIntoPathPatternTreeMap( + PatternTreeMap<String, PatternTreeMapFactory.StringSerializer> intoPathPatternTreeMap) { + this.intoPathPatternTreeMap = intoPathPatternTreeMap; } - public Map<String, Map<String, PartialPath>> getDeviceToIntoPathMap() { - return deviceToIntoPathMap; + public Map<PartialPath, Boolean> getIntoDeviceToAlignedMap() { + return intoDeviceToAlignedMap; } - public void setDeviceToIntoPathMap(Map<String, Map<String, PartialPath>> deviceToIntoPathMap) { - this.deviceToIntoPathMap = deviceToIntoPathMap; + public void setIntoDeviceToAlignedMap(Map<PartialPath, Boolean> intoDeviceToAlignedMap) { + this.intoDeviceToAlignedMap = intoDeviceToAlignedMap; } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java index d0d32de590..2731e84907 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/AnalyzeVisitor.java @@ -29,6 +29,7 @@ import org.apache.iotdb.commons.partition.SchemaPartition; import org.apache.iotdb.commons.path.MeasurementPath; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; +import org.apache.iotdb.commons.path.PatternTreeMap; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.engine.storagegroup.TsFileResource; import org.apache.iotdb.db.engine.storagegroup.TsFileResourceStatus; @@ -38,6 +39,7 @@ import org.apache.iotdb.db.exception.metadata.template.TemplateImcompatibeExcept import org.apache.iotdb.db.exception.sql.MeasurementNotExistException; import org.apache.iotdb.db.exception.sql.SemanticException; import org.apache.iotdb.db.exception.sql.StatementAnalyzeException; +import org.apache.iotdb.db.metadata.path.PatternTreeMapFactory; import org.apache.iotdb.db.metadata.template.Template; import org.apache.iotdb.db.mpp.common.MPPQueryContext; import org.apache.iotdb.db.mpp.common.header.ColumnHeader; @@ -56,9 +58,10 @@ import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.OrderByParameter; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.StatementNode; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; -import org.apache.iotdb.db.mpp.plan.statement.component.AlignByTimeIntoComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent; +import org.apache.iotdb.db.mpp.plan.statement.component.IntoComponent; +import org.apache.iotdb.db.mpp.plan.statement.component.IntoItem; import org.apache.iotdb.db.mpp.plan.statement.component.Ordering; import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; import org.apache.iotdb.db.mpp.plan.statement.component.SortItem; @@ -102,6 +105,7 @@ import org.apache.iotdb.db.mpp.plan.statement.metadata.template.ShowSchemaTempla import org.apache.iotdb.db.mpp.plan.statement.sys.ExplainStatement; import org.apache.iotdb.db.mpp.plan.statement.sys.ShowVersionStatement; import org.apache.iotdb.db.mpp.plan.statement.sys.sync.ShowPipeSinkTypeStatement; +import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.db.query.control.SessionManager; import org.apache.iotdb.db.utils.FileLoaderUtils; import org.apache.iotdb.db.utils.TimePartitionUtils; @@ -111,7 +115,6 @@ import org.apache.iotdb.tsfile.file.header.ChunkHeader; import org.apache.iotdb.tsfile.file.metadata.IChunkMetadata; import org.apache.iotdb.tsfile.file.metadata.TimeseriesMetadata; import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType; -import org.apache.iotdb.db.qp.constant.SQLConstant; import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType; import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding; import org.apache.iotdb.tsfile.read.TsFileSequenceReader; @@ -140,6 +143,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TimeZone; +import java.util.regex.Matcher; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkState; @@ -233,6 +237,8 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analyzeDeviceToSource(analysis, queryStatement); analyzeDeviceView(analysis, queryStatement, outputExpressions); + + analyzeInto(analysis, queryStatement, deviceSet, outputExpressions); } else { outputExpressions = analyzeSelect(analysis, queryStatement, schemaTree); @@ -251,6 +257,8 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analyzeSourceTransform(analysis, queryStatement); analyzeSource(analysis, queryStatement); + + analyzeInto(analysis, queryStatement, outputExpressions); } analyzeGroupBy(analysis, queryStatement); @@ -988,40 +996,212 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> return partitionFetcher.getDataPartition(sgNameToQueryParamsMap); } - private void analyzeInto(Analysis analysis, List<Pair<Expression, String>> outputExpressions) { - Map<String, PartialPath> outputColumnToIntoPathMap = new HashMap<>(); + private void analyzeInto( + Analysis analysis, + QueryStatement queryStatement, + Set<PartialPath> deviceSet, + List<Pair<Expression, String>> outputExpressions) { List<Expression> outputColumns = - outputExpressions.stream() - .map(Pair::getLeft) - .collect(Collectors.toCollection(ArrayList::new)); - AlignByTimeIntoComponent intoComponent = - ((AlignByTimeIntoComponent) ((QueryStatement) analysis.getStatement()).getIntoComponent()); - List<PartialPath> intoPaths = intoComponent.getIntoPaths(); - boolean isAligned = intoComponent.isAligned(); - + outputExpressions.stream() + .map(Pair::getLeft) + .collect(Collectors.toCollection(ArrayList::new)); boolean isAllRawSeriesQuery = checkIsAllRawSeriesQuery(outputColumns); + + IntoComponent intoComponent = queryStatement.getIntoComponent(); + List<IntoItem> intoItems = intoComponent.getIntoItems(); + + Map<PartialPath, PartialPath> sourceDeviceToTargetDeviceMap = new HashMap<>(); + List<PartialPath> sourceDevices = new ArrayList<>(deviceSet); + if (intoComponent.isDeviceExistPlaceholder()) { + if (intoItems.size() > 1) { + throw new SemanticException(""); + } + + PartialPath deviceTemplate = intoItems.get(0).getIntoDevice(); + for (int i = 0; i < sourceDevices.size(); i++) { + PartialPath sourceDevice = sourceDevices.get(i); + PartialPath targetDevice = constructIntoDevice(sourceDevice, deviceTemplate); + if (sourceDeviceToTargetDeviceMap.containsKey(sourceDevice) + && !sourceDeviceToTargetDeviceMap.get(sourceDevice).equals(targetDevice)) { + throw new SemanticException(""); + } + sourceDeviceToTargetDeviceMap.put(sourceDevices.get(i), intoItems.get(i).getIntoDevice()); + } + } else { + if (intoItems.size() != sourceDevices.size()) { + throw new SemanticException(""); + } + + for (int i = 0; i < sourceDevices.size(); i++) { + PartialPath sourceDevice = sourceDevices.get(i); + PartialPath targetDevice = intoItems.get(i).getIntoDevice(); + if (sourceDeviceToTargetDeviceMap.containsKey(sourceDevice) + && !sourceDeviceToTargetDeviceMap.get(sourceDevice).equals(targetDevice)) { + throw new SemanticException(""); + } + sourceDeviceToTargetDeviceMap.put(sourceDevices.get(i), intoItems.get(i).getIntoDevice()); + } + } + + PatternTreeMap<String, PatternTreeMapFactory.StringSerializer> intoPathPatternTreeMap = + PatternTreeMapFactory.getIntoPathPatternTreeMap(); + if (isAllRawSeriesQuery) { + + } else { + // disable placeholder + for (IntoItem intoItem : intoItems) { + if (intoItem.isMeasurementsExistPlaceholder()) { + throw new SemanticException( + "select into: placeholders can only be used in raw timeseries data queries."); + } + } + + List<PartialPath> intoPaths = + intoItems.stream() + .map(IntoItem::getIntoPaths) + .flatMap(List::stream) + .collect(Collectors.toList()); + + // check quantity consistency if (intoPaths.size() != outputColumns.size()) { throw new SemanticException( - "select into: the number of source paths and the number of target paths should be the same."); + "select into: the number of source columns and the number of target paths should be the same."); } - if (intoPaths.size() > new HashSet<>(intoPaths).size()) { - throw new SemanticException( - "select into: target paths in into clause should be different."); + + for (int i = 0; i < intoPaths.size(); i++) { + if (intoPathPatternTreeMap.getOverlapped(intoPaths.get(i)).size() > 1) { + throw new SemanticException( + "select into: target paths in into clause should be different."); + } + intoPathPatternTreeMap.append(intoPaths.get(i), outputColumns.get(i).toString()); } - for (int i = 0; i < outputColumns.size(); i++) { - outputColumnToIntoPathMap.put( - outputColumns.get(i).toString(), - constructIntoPath(analysis, outputColumns.get(i), intoPaths.get(i), isAligned)); + } + analyzeIntoDevices(analysis, intoItems); + analysis.setIntoPathPatternTreeMap(intoPathPatternTreeMap); + } + + private PartialPath constructIntoDevice(PartialPath sourceDevice, PartialPath deviceTemplate) { + String[] sourceNodes = sourceDevice.getNodes(); + String[] templateNodes = deviceTemplate.getNodes(); + + List<String> targetNodes = new ArrayList<>(); + for (int nodeIndex = 0; nodeIndex < templateNodes.length; nodeIndex++) { + String curNode = templateNodes[nodeIndex]; + if (curNode.equals(DOUBLE_COLONS)) { + if (nodeIndex != templateNodes.length - 1) { + throw new SemanticException(""); + } + // copy + for (; nodeIndex < sourceNodes.length; nodeIndex++) { + targetNodes.add(sourceNodes[nodeIndex]); + } + break; } + + Matcher m = LEVELED_PATH_TEMPLATE_PATTERN.matcher(curNode); + String resNode = curNode; + while (m.find()) { + String param = m.group(); + int index; + try { + index = Integer.parseInt(param.substring(2, param.length() - 1).trim()); + } catch (NumberFormatException e) { + throw new SemanticException("select into: the i of ${i} should be an integer."); + } + if (index < 1 || index >= sourceNodes.length) { + throw new SemanticException( + "select into: the i of ${i} should be greater than 0 and equal to or less than the length of queried path prefix."); + } + } + targetNodes.add(resNode); + } + return new PartialPath(targetNodes.toArray(new String[0])); + } + + private void analyzeInto( + Analysis analysis, + QueryStatement queryStatement, + List<Pair<Expression, String>> outputExpressions) { + if (queryStatement.getIntoComponent() == null) { + return; + } + + List<Expression> outputColumns = + outputExpressions.stream() + .map(Pair::getLeft) + .collect(Collectors.toCollection(ArrayList::new)); + boolean isAllRawSeriesQuery = checkIsAllRawSeriesQuery(outputColumns); + + IntoComponent intoComponent = queryStatement.getIntoComponent(); + List<IntoItem> intoItems = intoComponent.getIntoItems(); + + PatternTreeMap<String, PatternTreeMapFactory.StringSerializer> intoPathPatternTreeMap = + PatternTreeMapFactory.getIntoPathPatternTreeMap(); + + if (isAllRawSeriesQuery) { + } else { + // disable placeholder + for (IntoItem intoItem : intoItems) { + if (intoItem.isDeviceExistPlaceholder() || intoItem.isMeasurementsExistPlaceholder()) { + throw new SemanticException( + "select into: placeholders can only be used in raw timeseries data queries."); + } + } + List<PartialPath> intoPaths = + intoItems.stream() + .map(IntoItem::getIntoPaths) + .flatMap(List::stream) + .collect(Collectors.toList()); + + // check quantity consistency + if (intoPaths.size() != outputColumns.size()) { + throw new SemanticException( + "select into: the number of source columns and the number of target paths should be the same."); + } + + for (int i = 0; i < intoPaths.size(); i++) { + intoPathPatternTreeMap.append(intoPaths.get(i), outputColumns.get(i).toString()); + if (intoPathPatternTreeMap.getOverlapped(intoPaths.get(i)).size() > 1) { + throw new SemanticException( + "select into: target paths in into clause should be different."); + } + } } - analysis.setOutputColumnToIntoPathMap(outputColumnToIntoPathMap); + analyzeIntoDevices(analysis, intoItems); + analysis.setIntoPathPatternTreeMap(intoPathPatternTreeMap); + } + + private void analyzeIntoDevices(Analysis analysis, List<IntoItem> intoItems) { + Map<PartialPath, Boolean> intoDeviceToAlignedMap = new HashMap<>(); + for (IntoItem intoItem : intoItems) { + PartialPath devicePath = intoItem.getIntoDevice(); + boolean isAligned = intoItem.isAligned(); + if (intoDeviceToAlignedMap.containsKey(devicePath) + && intoDeviceToAlignedMap.get(devicePath) != isAligned) { + throw new SemanticException( + String.format( + "select into: inconsistent alignment property specified for device '%s'.", + devicePath)); + } + intoDeviceToAlignedMap.put(devicePath, isAligned); + } + analysis.setIntoDeviceToAlignedMap(intoDeviceToAlignedMap); + } + + private boolean checkIsAllRawSeriesQuery(List<Expression> expressions) { + for (Expression expression : expressions) { + if (!(expression instanceof TimeSeriesOperand)) { + return true; + } + } + return false; } private PartialPath constructIntoPath( - Analysis analysis, Expression outputColumn, PartialPath path, boolean isAligned) { + Analysis analysis, Expression outputColumn, PartialPath path, boolean isAligned) { if (!path.startWith(SQLConstant.ROOT)) { throw new SemanticException("select into: "); } @@ -1034,15 +1214,6 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> return new MeasurementPath(path, analysis.getType(outputColumn), isAligned); } - private void analyzeInto( - Analysis analysis, - List<Pair<Expression, String>> outputExpressions, - Map<String, Set<String>> deviceToMeasurementsMap) { - Map<String, Map<String, PartialPath>> deviceToIntoPathMap = new HashMap<>(); - - analysis.setDeviceToIntoPathMap(deviceToIntoPathMap); - } - /** * Check datatype consistency in ALIGN BY DEVICE. * diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java index 38391e156d..c4f9ddf867 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java @@ -63,14 +63,14 @@ import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression; import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression; import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression; import org.apache.iotdb.db.mpp.plan.statement.Statement; -import org.apache.iotdb.db.mpp.plan.statement.component.AlignByDeviceIntoComponent; -import org.apache.iotdb.db.mpp.plan.statement.component.AlignByTimeIntoComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy; import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByLevelComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent; import org.apache.iotdb.db.mpp.plan.statement.component.HavingCondition; +import org.apache.iotdb.db.mpp.plan.statement.component.IntoComponent; +import org.apache.iotdb.db.mpp.plan.statement.component.IntoItem; import org.apache.iotdb.db.mpp.plan.statement.component.OrderByComponent; import org.apache.iotdb.db.mpp.plan.statement.component.Ordering; import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn; @@ -1369,31 +1369,27 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> { // parse INTO clause private void parseIntoClause(IoTDBSqlParser.IntoClauseContext ctx) { - boolean isAligned = ctx.ALIGNED() != null; - if (ctx.intoDeviceAndMeasurement().size() > 0) { - List<Pair<PartialPath, List<PartialPath>>> intoDeviceAndMeasurementList = new ArrayList<>(); - for (IoTDBSqlParser.IntoDeviceAndMeasurementContext intoDeviceAndMeasurementContext : - ctx.intoDeviceAndMeasurement()) { - PartialPath intoDevice = parseIntoPath(intoDeviceAndMeasurementContext.intoPath()); - List<PartialPath> intoMeasurements = - intoDeviceAndMeasurementContext.nodeNameInIntoPath().stream() - .map( - nodeNameInIntoPathContext -> - new PartialPath(parseNodeNameInIntoPath(nodeNameInIntoPathContext), false)) - .collect(Collectors.toList()); - intoDeviceAndMeasurementList.add(new Pair<>(intoDevice, intoMeasurements)); - } - queryStatement.setIntoComponent( - new AlignByDeviceIntoComponent(intoDeviceAndMeasurementList, isAligned)); - } else { - List<PartialPath> intoPaths = new ArrayList<>(); - for (IoTDBSqlParser.IntoPathContext intoPathContext : ctx.intoPath()) { - intoPaths.add(parseIntoPath(intoPathContext)); + if (ctx.intoItem().size() > 0) { + List<IntoItem> intoItems = new ArrayList<>(); + for (IoTDBSqlParser.IntoItemContext intoItemContext : ctx.intoItem()) { + intoItems.add(parseIntoItem(intoItemContext)); } - queryStatement.setIntoComponent(new AlignByTimeIntoComponent(intoPaths, isAligned)); + queryStatement.setIntoComponent(new IntoComponent(intoItems)); + } else { + throw new SemanticException("The syntax of SELECT INTO statement has changed from v0.14"); } } + private IntoItem parseIntoItem(IoTDBSqlParser.IntoItemContext intoItemContext) { + boolean isAligned = intoItemContext.ALIGNED() != null; + PartialPath intoDevice = parseIntoPath(intoItemContext.intoPath()); + List<String> intoMeasurements = + intoItemContext.nodeNameInIntoPath().stream() + .map(this::parseNodeNameInIntoPath) + .collect(Collectors.toList()); + return new IntoItem(intoDevice, intoMeasurements, isAligned); + } + private PartialPath parseIntoPath(IoTDBSqlParser.IntoPathContext intoPathContext) { if (intoPathContext instanceof IoTDBSqlParser.FullPathInIntoPathContext) { return parseFullPathInIntoPath((IoTDBSqlParser.FullPathInIntoPathContext) intoPathContext); diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByDeviceIntoComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByDeviceIntoComponent.java deleted file mode 100644 index 4cf1b14b78..0000000000 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByDeviceIntoComponent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.statement.component; - -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.tsfile.utils.Pair; - -import java.util.List; - -public class AlignByDeviceIntoComponent extends IntoComponent { - - private final List<Pair<PartialPath, List<PartialPath>>> intoDeviceAndMeasurementList; - - public AlignByDeviceIntoComponent( - List<Pair<PartialPath, List<PartialPath>>> intoDeviceAndMeasurementList, boolean isAligned) { - super(isAligned); - this.intoDeviceAndMeasurementList = intoDeviceAndMeasurementList; - } - - public List<Pair<PartialPath, List<PartialPath>>> getIntoDeviceAndMeasurementList() { - return intoDeviceAndMeasurementList; - } -} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByTimeIntoComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByTimeIntoComponent.java deleted file mode 100644 index c45a1c082d..0000000000 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/AlignByTimeIntoComponent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.statement.component; - -import org.apache.iotdb.commons.path.PartialPath; - -import java.util.List; - -public class AlignByTimeIntoComponent extends IntoComponent { - - private final List<PartialPath> intoPaths; - - public AlignByTimeIntoComponent(List<PartialPath> intoPaths, boolean isAligned) { - super(isAligned); - this.intoPaths = intoPaths; - } - - public List<PartialPath> getIntoPaths() { - return intoPaths; - } -} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java index 1e22c6fb9e..1eaa3ff073 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java @@ -19,16 +19,29 @@ package org.apache.iotdb.db.mpp.plan.statement.component; +import org.apache.iotdb.db.mpp.plan.statement.StatementNode; + +import java.util.List; + /** This class maintains information of {@code INTO} clause. */ -public abstract class IntoComponent { +public class IntoComponent extends StatementNode { - protected final boolean isAligned; + private final List<IntoItem> intoItems; + + public IntoComponent(List<IntoItem> intoItems) { + this.intoItems = intoItems; + } - protected IntoComponent(boolean isAligned) { - this.isAligned = isAligned; + public List<IntoItem> getIntoItems() { + return intoItems; } - public boolean isAligned() { - return isAligned; + public boolean isDeviceExistPlaceholder() { + for (IntoItem intoItem : intoItems) { + if (intoItem.isDeviceExistPlaceholder()) { + return true; + } + } + return false; } } diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java new file mode 100644 index 0000000000..4efafe3cef --- /dev/null +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java @@ -0,0 +1,73 @@ +/* + * 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.statement.component; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.mpp.plan.statement.StatementNode; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.apache.iotdb.commons.conf.IoTDBConstant.DOUBLE_COLONS; +import static org.apache.iotdb.commons.conf.IoTDBConstant.LEVELED_PATH_TEMPLATE_PATTERN; + +public class IntoItem extends StatementNode { + + private final PartialPath intoDevice; + private final List<String> intoMeasurements; + private final boolean isAligned; + + public IntoItem(PartialPath intoDevice, List<String> intoMeasurements, boolean isAligned) { + this.intoDevice = intoDevice; + this.intoMeasurements = intoMeasurements; + this.isAligned = isAligned; + } + + public PartialPath getIntoDevice() { + return intoDevice; + } + + public List<String> getIntoMeasurements() { + return intoMeasurements; + } + + public boolean isAligned() { + return isAligned; + } + + public boolean isDeviceExistPlaceholder() { + return intoDevice.containNode(DOUBLE_COLONS) + || LEVELED_PATH_TEMPLATE_PATTERN.matcher(intoDevice.getFullPath()).find(); + } + + public boolean isMeasurementsExistPlaceholder() { + for (String measurement : intoMeasurements) { + if (measurement.equals(DOUBLE_COLONS) + || LEVELED_PATH_TEMPLATE_PATTERN.matcher(measurement).find()) { + return true; + } + } + return false; + } + + public List<PartialPath> getIntoPaths() { + return intoMeasurements.stream().map(intoDevice::concatNode).collect(Collectors.toList()); + } +} diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java index ddab00ec44..d0c05930be 100644 --- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java +++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java @@ -27,8 +27,6 @@ import org.apache.iotdb.db.mpp.plan.expression.Expression; import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand; import org.apache.iotdb.db.mpp.plan.statement.Statement; import org.apache.iotdb.db.mpp.plan.statement.StatementVisitor; -import org.apache.iotdb.db.mpp.plan.statement.component.AlignByDeviceIntoComponent; -import org.apache.iotdb.db.mpp.plan.statement.component.AlignByTimeIntoComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent; import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent; import org.apache.iotdb.db.mpp.plan.statement.component.GroupByLevelComponent; @@ -399,14 +397,6 @@ public class QueryStatement extends Statement { if (isLastQuery()) { throw new SemanticException("select into: last clauses are not supported."); } - if (isAlignByDevice() && intoComponent instanceof AlignByTimeIntoComponent) { - throw new SemanticException( - "select into: target path is illegal, expected: full path or suffix path"); - } - if (isAlignByTime() && intoComponent instanceof AlignByDeviceIntoComponent) { - throw new SemanticException( - "select into: target path is illegal, expected: target device and measurements"); - } } } diff --git a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java index 767d499d3d..ae7d4d96bb 100644 --- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java +++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java @@ -1271,25 +1271,31 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> { private SelectIntoOperator parseAndConstructSelectIntoOperator( IoTDBSqlParser.SelectStatementContext ctx) { + IoTDBSqlParser.IntoClauseContext intoClauseContext = ctx.intoClause(); + if (intoClauseContext.intoItem().size() > 0) { + throw new SQLParserException( + "The new syntax of SELECT INTO statement will be supported starting from v0.14"); + } + if (queryOp.getFromComponent().getPrefixPaths().size() != 1) { throw new SQLParserException( "select into: the number of prefix paths in the from clause should be 1."); } - int sourcePathsCount = queryOp.getSelectComponent().getResultColumns().size(); - if (sourcePathsCount != ctx.intoClause().intoPath().size()) { + if (sourcePathsCount != intoClauseContext.intoPath().size()) { throw new SQLParserException( "select into: the number of source paths and the number of target paths should be the same."); } SelectIntoOperator selectIntoOperator = new SelectIntoOperator(); selectIntoOperator.setQueryOperator(queryOp); + List<PartialPath> intoPaths = new ArrayList<>(); for (int i = 0; i < sourcePathsCount; ++i) { - intoPaths.add(parseIntoPath(ctx.intoClause().intoPath(i))); + intoPaths.add(parseIntoPath(intoClauseContext.intoPath(i))); } selectIntoOperator.setIntoPaths(intoPaths); - selectIntoOperator.setIntoPathsAligned(ctx.intoClause().ALIGNED() != null); + selectIntoOperator.setIntoPathsAligned(intoClauseContext.ALIGNED() != null); return selectIntoOperator; }
