blob: deebefa9058d87b6a034762941117e358c6d6ccc [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.rewrite;
import com.google.common.collect.Lists;
import java.util.List;
import org.apache.impala.analysis.Analyzer;
import org.apache.impala.analysis.Expr;
import org.apache.impala.analysis.InPredicate;
import org.apache.impala.analysis.Subquery;
/**
* Coalesces disjunctive equality predicates to an IN predicate, and merges compatible
* equality or IN predicates into an existing IN predicate.
* Examples:
* (C=1) OR (C=2) OR (C=3) OR (C=4) -> C IN(1, 2, 3, 4)
* (X+Y = 5) OR (X+Y = 6) -> X+Y IN (5, 6)
* (A = 1) OR (A IN (2, 3)) -> A IN (1, 2, 3)
* (B IN (1, 2)) OR (B IN (3, 4)) -> B IN (1, 2, 3, 4)
*/
public class EqualityDisjunctsToInRule implements ExprRewriteRule {
public static ExprRewriteRule INSTANCE = new EqualityDisjunctsToInRule();
@Override
public Expr apply(Expr expr, Analyzer analyzer) {
if (!Expr.IS_OR_PREDICATE.apply(expr)) return expr;
Expr inAndOtherExpr = rewriteInAndOtherExpr(expr.getChild(0), expr.getChild(1));
if (inAndOtherExpr != null) return inAndOtherExpr;
Expr orChildExpr = rewriteEqEqPredicate(expr.getChild(0), expr.getChild(1));
if (orChildExpr != null) return orChildExpr;
return expr;
}
/**
* Takes the children of an OR predicate and attempts to combine them into a single IN
* predicate. The transformation is applied if one of the children is an IN predicate
* and the other child is a compatible IN predicate or equality predicate. Returns the
* transformed expr or null if no transformation was possible.
*/
private Expr rewriteInAndOtherExpr(Expr child0, Expr child1) {
InPredicate inPred = null;
Expr otherPred = null;
if (child0 instanceof InPredicate) {
inPred = (InPredicate) child0;
otherPred = child1;
} else if (child1 instanceof InPredicate) {
inPred = (InPredicate) child1;
otherPred = child0;
}
if (inPred == null || inPred.isNotIn() || inPred.contains(Subquery.class) ||
!inPred.getChild(0).equals(otherPred.getChild(0))) {
return null;
}
// other predicate can be OR predicate or IN predicate
List<Expr> newInList = Lists.newArrayList(
inPred.getChildren().subList(1, inPred.getChildren().size()));
if (Expr.IS_EXPR_EQ_LITERAL_PREDICATE.apply(otherPred)) {
if (newInList.size() + 1 == Expr.EXPR_CHILDREN_LIMIT) return null;
newInList.add(otherPred.getChild(1));
} else if (otherPred instanceof InPredicate && !((InPredicate) otherPred).isNotIn()
&& !otherPred.contains(Subquery.class)) {
if (newInList.size() + otherPred.getChildren().size() > Expr.EXPR_CHILDREN_LIMIT) {
return null;
}
newInList.addAll(
otherPred.getChildren().subList(1, otherPred.getChildren().size()));
} else {
return null;
}
return new InPredicate(inPred.getChild(0), newInList, false);
}
/**
* Takes the children of an OR predicate and attempts to combine them into a single IN predicate.
* The transformation is applied if both children are equality predicates with a literal on the
* right hand side.
* Returns the transformed expr or null if no transformation was possible.
*/
private Expr rewriteEqEqPredicate(Expr child0, Expr child1) {
if (!Expr.IS_EXPR_EQ_LITERAL_PREDICATE.apply(child0)) return null;
if (!Expr.IS_EXPR_EQ_LITERAL_PREDICATE.apply(child1)) return null;
if (!child0.getChild(0).equals(child1.getChild(0))) return null;
Expr newExpr = new InPredicate(child0.getChild(0),
Lists.newArrayList(child0.getChild(1), child1.getChild(1)), false);
return newExpr;
}
}