blob: 731566502c921aca0b144aa5ccbffc16b2e557d7 [file] [log] [blame]
/*
* 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));
}
}
}