blob: ac48f6724d97db0284a527eca94a1ccf397cbd07 [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.impala.analysis;
import com.google.common.base.Preconditions;
import org.apache.impala.common.Pair;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Helper class to classify constant predicates and propagate them
* to other eligible predicates.
*/
public class ConstantPredicateHandler {
// Equality constant predicates indexed by their position in the
// original list of conjuncts. Boolean flag tracks whether this
// equality predicate itself was rewritten.
private Map<Integer, Pair<Expr, Boolean>> equalityPreds_;
// For range predicates, maintain a list of exprs to handle constant predicates on
// the same slot, for example: mydate >= '2019-01-01' AND mydate <= '2020-01-01',
// since both need to be propagated together
private Map<SlotRef, List<Expr>> rangePreds_;
// certain constant predicates are meant for date/timestamp types only
private Map<Integer, Expr> dateTimePreds_;
public ConstantPredicateHandler() {
equalityPreds_ = new HashMap<>();
rangePreds_ = new HashMap<>();
dateTimePreds_ = new HashMap<>();
}
/**
* Classify predicates of type 'slotRef <op> constant' into separate lists.
* Here <op> can be a comparison operator of type '=, <, <=, >. >='.
* The range predicates are kept in a separate list from the equality
* predicates. The candidates BitSet is used to determine which members of
* conjuncts are considered for propagation.
*/
public void classifyPredicates(List<Expr> conjuncts, BitSet candidates) {
for (int i = candidates.nextSetBit(0); i >= 0;
i = candidates.nextSetBit(i+1)) {
Expr conjunct = conjuncts.get(i);
if (!Expr.IS_BINARY_PREDICATE.apply(conjunct)) continue;
BinaryPredicate bp = (BinaryPredicate) conjunct;
// equality and range operators in the constant predicate
// are considered for propagation
boolean isRangeOp = BinaryPredicate.IS_RANGE_PREDICATE.apply(bp);
if (!isRangeOp && bp.getOp() != BinaryPredicate.Operator.EQ) continue;
SlotRef slotRef = bp.getBoundSlot();
if (slotRef == null || !bp.getChild(1).isConstant()) {
// save the date/timestamp predicates that are not constants themselves
// in a separate list since they may be the target of propagation later
if (isCandidateDateTimeExpr(bp)) {
dateTimePreds_.put(i, bp);
}
continue;
}
Expr constant = bp.getChild(1);
// for range predicates, only date and timestamp constants are allowed
if (isRangeOp && !(constant instanceof DateLiteral ||
constant instanceof TimestampLiteral)) {
continue;
}
if (isRangeOp) {
List<Expr> rangePreds = rangePreds_.getOrDefault(slotRef, new ArrayList<>());
rangePreds.add(bp);
rangePreds_.put(slotRef, rangePreds);
} else {
equalityPreds_.put(i, new Pair<>(bp, false));
}
}
}
/**
* Propagate equality constant predicates to other conjuncts. Propagate
* range constant predicates to conjuncts involving date and timestamp
* columns. Populate the keepConjuncts list with the original dateTime
* predicates that need to be preserved for correctness in certain cases.
*/
public void propagateConstantPreds(List<Expr> conjuncts, BitSet changed,
List<Expr> keepConjuncts, Analyzer analyzer) {
for (Map.Entry<Integer, Pair<Expr, Boolean>> e : equalityPreds_.entrySet()) {
Pair<Expr, Boolean> value = e.getValue();
// skip this predicate if it itself was previously rewritten
if (value.second) continue;
BinaryPredicate bp = (BinaryPredicate) value.first;
SlotRef slotRef = bp.getBoundSlot();
Preconditions.checkNotNull(slotRef);
Expr subst = bp.getSlotBinding(slotRef.getSlotId());
ExprSubstitutionMap smap = new ExprSubstitutionMap();
smap.put(slotRef, subst);
for (int j = 0; j < conjuncts.size(); ++j) {
Expr toRewrite = conjuncts.get(j);
// Don't rewrite with our own substitution!
if (toRewrite == bp) continue;
Expr rewritten = toRewrite.substitute(smap, analyzer, true);
if (!rewritten.equals(toRewrite)) {
conjuncts.set(j, rewritten);
changed.set(j, true);
if (equalityPreds_.containsKey(j)) {
equalityPreds_.get(j).second = true;
}
}
}
}
if (dateTimePreds_.size() == 0) return;
// Outer loop is different compared to the equality predicates above.
// Since there could be more than 1 constant range predicate that needs
// propagation to a particular target, we start with the target date/time
// expr and apply all the possible range predicate substitutions.
for (Map.Entry<Integer, Expr> dtEntry : dateTimePreds_.entrySet()) {
int index = dtEntry.getKey();
Expr dtExpr = dtEntry.getValue();
List<Expr> rewrittenExprs = new ArrayList<>();
for (Map.Entry<SlotRef, List<Expr>> e : rangePreds_.entrySet()) {
SlotRef slotRef = e.getKey();
List<Expr> rangePredsList = e.getValue();
if (rangePredsList.size() == 0) continue;
for (Expr rangePred : rangePredsList) {
// Don't rewrite with our own substitution!
if (rangePred == dtExpr) continue;
BinaryPredicate bp = (BinaryPredicate) rangePred;
Expr subst = bp.getSlotBinding(slotRef.getSlotId());
ExprSubstitutionMap smap = new ExprSubstitutionMap();
smap.put(slotRef, subst);
// make a copy since we may need to re-use the original
Expr toRewrite = dtExpr.clone();
toRewrite = rewriteWithOp((BinaryPredicate) toRewrite, bp.getOp(), analyzer);
Expr rewritten = toRewrite.substitute(smap, analyzer, true);
if (!rewritten.equals(toRewrite)) {
rewrittenExprs.add(rewritten);
}
}
if (rewrittenExprs.size() > 0) {
keepConjuncts.add(dtExpr);
Expr finalRewritten =
CompoundPredicate.createConjunctivePredicate(rewrittenExprs);
conjuncts.set(index, finalRewritten);
changed.set(index, true);
}
}
}
}
public static boolean isCandidateDateTimeExpr(BinaryPredicate bp) {
if (bp.getChild(0).getType().isDateOrTimeType()
&& bp.getOp() == BinaryPredicate.Operator.EQ
&& bp.getChild(1) instanceof CastExpr) {
return true;
}
return false;
}
private Expr rewriteWithOp(BinaryPredicate pred, BinaryPredicate.Operator newOp,
Analyzer analyzer) {
if (pred.getOp() == newOp) return pred;
BinaryPredicate newPred = new BinaryPredicate(newOp, pred.getChild(0),
pred.getChild(1));
newPred.analyzeNoThrow(analyzer);
return newPred;
}
}