| /* |
| * 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.qp.utils; |
| |
| import org.apache.iotdb.commons.exception.MetadataException; |
| import org.apache.iotdb.commons.path.MeasurementPath; |
| import org.apache.iotdb.commons.path.PartialPath; |
| import org.apache.iotdb.db.conf.IoTDBDescriptor; |
| import org.apache.iotdb.db.exception.query.LogicalOptimizeException; |
| import org.apache.iotdb.db.exception.query.PathNumOverLimitException; |
| import org.apache.iotdb.db.mpp.plan.expression.Expression; |
| import org.apache.iotdb.db.mpp.plan.expression.ResultColumn; |
| import org.apache.iotdb.db.qp.logical.crud.QueryOperator; |
| import org.apache.iotdb.db.qp.strategy.optimizer.ConcatPathOptimizer; |
| import org.apache.iotdb.db.service.IoTDB; |
| import org.apache.iotdb.tsfile.utils.Pair; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** Removes wildcards (applying memory control and slimit/soffset control) */ |
| public class WildcardsRemover { |
| |
| private int soffset = 0; |
| private int currentOffset = 0; |
| private int currentLimit = |
| IoTDBDescriptor.getInstance().getConfig().getMaxQueryDeduplicatedPathNum() + 1; |
| |
| /** Records the path number that the SchemaProcessor totally returned. */ |
| private int consumed = 0; |
| |
| /** |
| * Since IoTDB v0.13, all DDL and DML use patternMatch as default. Before IoTDB v0.13, all DDL and |
| * DML use prefixMatch. |
| */ |
| private boolean isPrefixMatch; |
| |
| public WildcardsRemover(QueryOperator queryOperator) { |
| isPrefixMatch = queryOperator.isPrefixMatchPath(); |
| if (queryOperator.getSpecialClauseComponent() != null) { |
| soffset = queryOperator.getSpecialClauseComponent().getSeriesOffset(); |
| currentOffset = soffset; |
| final int slimit = queryOperator.getSpecialClauseComponent().getSeriesLimit(); |
| currentLimit = slimit == 0 ? currentLimit : Math.min(slimit, currentLimit); |
| } |
| } |
| |
| private WildcardsRemover(boolean isPrefixMatch) { |
| this.isPrefixMatch = isPrefixMatch; |
| } |
| |
| public List<MeasurementPath> removeWildcardFrom(PartialPath path) |
| throws LogicalOptimizeException { |
| try { |
| Pair<List<MeasurementPath>, Integer> pair = |
| IoTDB.schemaProcessor.getMeasurementPathsWithAlias( |
| path, currentLimit, currentOffset, isPrefixMatch); |
| consumed += pair.right; |
| currentOffset -= Math.min(currentOffset, pair.right); |
| currentLimit -= pair.left.size(); |
| return pair.left; |
| } catch (MetadataException e) { |
| throw new LogicalOptimizeException("error occurred when removing star: " + e.getMessage()); |
| } |
| } |
| |
| public List<List<Expression>> removeWildcardsFrom(List<Expression> expressions) |
| throws LogicalOptimizeException { |
| // One by one, remove the wildcards from the input expressions. In most cases, an expression |
| // will produce multiple expressions after removing the wildcards. We use extendedExpressions to |
| // collect the produced expressions. |
| List<List<Expression>> extendedExpressions = new ArrayList<>(); |
| for (Expression originExpression : expressions) { |
| List<Expression> actualExpressions = new ArrayList<>(); |
| originExpression.removeWildcards(new WildcardsRemover(isPrefixMatch), actualExpressions); |
| if (actualExpressions.isEmpty()) { |
| // Let's ignore the eval of the function which has at least one non-existence series as |
| // input. See IOTDB-1212: https://github.com/apache/iotdb/pull/3101 |
| return Collections.emptyList(); |
| } |
| extendedExpressions.add(actualExpressions); |
| } |
| |
| // Calculate the Cartesian product of extendedExpressions to get the actual expressions after |
| // removing all wildcards. We use actualExpressions to collect them. |
| List<List<Expression>> actualExpressions = new ArrayList<>(); |
| ConcatPathOptimizer.cartesianProduct( |
| extendedExpressions, actualExpressions, 0, new ArrayList<>()); |
| |
| // Apply the soffset & slimit control to the actualExpressions and return the remaining |
| // expressions. |
| List<List<Expression>> remainingExpressions = new ArrayList<>(); |
| for (List<Expression> actualExpression : actualExpressions) { |
| if (currentOffset != 0) { |
| --currentOffset; |
| continue; |
| } else if (currentLimit != 0) { |
| --currentLimit; |
| } else { |
| break; |
| } |
| remainingExpressions.add(actualExpression); |
| } |
| consumed += actualExpressions.size(); |
| return remainingExpressions; |
| } |
| |
| /** @return should break the loop or not */ |
| public boolean checkIfPathNumberIsOverLimit(List<ResultColumn> resultColumns) |
| throws PathNumOverLimitException { |
| if (resultColumns.size() |
| > IoTDBDescriptor.getInstance().getConfig().getMaxQueryDeduplicatedPathNum()) { |
| throw new PathNumOverLimitException(); |
| } |
| return currentLimit == 0; |
| } |
| |
| public void checkIfSoffsetIsExceeded(List<ResultColumn> resultColumns) |
| throws LogicalOptimizeException { |
| if (consumed == 0 ? soffset != 0 : resultColumns.isEmpty()) { |
| throw new LogicalOptimizeException( |
| String.format( |
| "The value of SOFFSET (%d) is equal to or exceeds the number of sequences (%d) that can actually be returned.", |
| soffset, consumed)); |
| } |
| } |
| } |