/*
* 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.pig.newplan.logical.rules;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import org.apache.pig.impl.logicalLayer.FrontendException;
import org.apache.pig.impl.util.Pair;
import org.apache.pig.newplan.logical.expression.*;
import org.apache.pig.newplan.logical.relational.LOFilter;
import org.apache.pig.newplan.logical.relational.LogicalPlan;
import org.apache.pig.newplan.logical.relational.LogicalRelationalOperator;
import org.apache.pig.newplan.ReverseDependencyOrderWalker;
import org.apache.pig.newplan.Operator;
import org.apache.pig.newplan.OperatorPlan;
import org.apache.pig.newplan.OperatorSubPlan;
import org.apache.pig.newplan.optimizer.Rule;
import org.apache.pig.newplan.optimizer.Transformer;
/**
* A FILTER logical expression simplifier
*
*/
public class LogicalExpressionSimplifier extends Rule {
private List<LOFilter> processedFilters = new ArrayList<LOFilter>();
enum DNFExpressionType {
AND, OR
}
public LogicalExpressionSimplifier(String n) {
super(n, false);
}
@Override
public Transformer getNewTransformer() {
return new LogicalExpressionSimplifierTransformer(processedFilters);
}
public static class LogicalExpressionSimplifierTransformer extends Transformer {
private static final String dnfCountAnnotationKey = "dnfSplitCount";
private OperatorPlan plan;
private List<LOFilter> processedFilters;
public LogicalExpressionSimplifierTransformer(List<LOFilter> processedFilters) {
this.processedFilters = processedFilters;
}
@Override
public boolean check(OperatorPlan matched) throws FrontendException {
LOFilter filter = (LOFilter)matched.getOperators().next();
// If the filter is already processed, we quit.
if (processedFilters.contains(filter))
return false;
processedFilters.add(filter);
return true;
}
@Override
public void transform(OperatorPlan plan) throws FrontendException {
Iterator<Operator> iter = plan.getOperators();
while (iter.hasNext()) {
Operator op = iter.next();
if (op instanceof LOFilter) {
LOFilter filter = (LOFilter) op;
LogicalExpressionPlan filterPlan = filter.getFilterPlan();
this.plan = ((OperatorSubPlan) plan).getBasePlan();
try {
// 1: evaluate constant expressions
ConstExpEvaluator constExpEvaluator = new ConstExpEvaluator(
filterPlan);
constExpEvaluator.visit();
NOTConversionVisitor notVisitor = new NOTConversionVisitor(
filterPlan);
// 2: convert away NOT through the DeMorgan's Law
notVisitor.visit();
// 3: DNF generation
DNFPlanGenerator dnfVisitor = new DNFPlanGenerator(
filterPlan);
dnfVisitor.visit();
OperatorPlan dnfPlan = dnfVisitor.getDNFPlan();
// 4: Trim the DNF tree
/**
* Then the DNF plan is trimmed according to the inference
* rules between the operands of the conjunctions first,
* and then between the operands of the disjunction in the
* DNF plan. If a leaf is trimmed, the counter, DNFSpliCounter
* , of the source of the proxy will be decremented. Basically,
* the DNF plan is used as a utility to determine if an
* original leaf expression can be trimmed from the original
* filter plan or not. If all proxies of the original leaf
* expression have been trimmed from the DNF plan, the original
* leaf expression can be trimmed from the original plan then.
* The point is that the DNF plan is not intended to replace
* the original filer plan since the DNF plan in general tends
* to be more expensive to evaluate than the original filter plan.
*/
checkDNFLeaves(dnfPlan);
// 5: Trim the original filterPlan
/**
* The original filter plan is traversed in a bottom-up manner
* so that if a leaf's DNFSpliCounter is zero, which means all
* of its proxies on DNF has been trimmed, the leaf will be trimmed.
* For non-leafs of "AND" or "OR" expressions, if one child survives,
* the child will be re-linked to the predecessor(s). If either or
* both children are trimmed, the non-leaf will be trimmed
* too. If the whole new filter plan is empty, the filter operator
* will be removed from the logical plan too.
*/
trimLogicalExpressionPlan(filterPlan);
}
catch (FrontendException e) {
return;
}
if (filterPlan.size() == 0) {
// the whole expression is simplified away so there is no reason for the FILTER itself
List<Operator> predList = this.plan.getPredecessors(op), sucList = this.plan.getSuccessors(op);
Operator[] sucs = sucList == null ? null
: sucList.toArray(new Operator[0]);
if (sucs != null) {
for (Operator suc : sucs)
this.plan.disconnect(op, suc);
}
if (predList != null) {
Operator[] preds = predList.toArray(new Operator[0]);
for (Operator pred : preds) {
this.plan.disconnect(pred, op);
if (sucs != null) {
for (Operator suc : sucs)
this.plan.connect(pred, suc);
}
}
}
this.plan.remove(filter);
}
}
}
}
static void incrDNFSplitCount(LogicalExpression le) {
Integer cnt = (Integer) le.getAnnotation(dnfCountAnnotationKey);
if (cnt == null)
cnt = 1;
le.annotate(dnfCountAnnotationKey, cnt.intValue()+1);
}
static void decrDNFSplitCount(LogicalExpression le) {
Integer cnt = (Integer) le.getAnnotation(dnfCountAnnotationKey);
if (cnt == null)
le.annotate(dnfCountAnnotationKey, 0);
else
le.annotate(dnfCountAnnotationKey, cnt.intValue()-1);
}
static boolean dnfTrimmed(LogicalExpression le) {
Integer cnt = (Integer) le.getAnnotation(dnfCountAnnotationKey);
if (cnt == null)
return false;
else
return (cnt == 0);
}
static int getSplitCount(LogicalExpression le) {
Integer cnt = (Integer) le.getAnnotation(dnfCountAnnotationKey);
if (cnt == null)
return 1;
return cnt;
}
private void checkDNFLeaves(OperatorPlan dnfPlan)
throws FrontendException {
List<Operator> roots = dnfPlan.getSources();
if (roots == null || roots.size() != 1)
throw new FrontendException(
"DNF root size is expected to be one");
Operator dnf = roots.get(0);
if (dnf instanceof AndExpression || (dnf instanceof DNFExpression && ((DNFExpression) dnf).type == DNFExpression.DNFExpressionType.AND)) {
handleDNFAnd(dnfPlan, dnf);
}
else if (dnf instanceof OrExpression || (dnf instanceof DNFExpression && ((DNFExpression) dnf).type == DNFExpression.DNFExpressionType.OR)) {
handleDNFOr(dnfPlan, dnf);
} else if (dnf instanceof ConstantExpression && (Boolean) (((ConstantExpression) dnf).getValue()))
decrDNFSplitCount((ConstantExpression) dnf);
}
static final byte ImplyLeft = 1,
ImplyRight = 2,
Exclusive = 4,
Equal = 8,
Complementary = 16,
Unknown = ~(ImplyLeft | ImplyRight | Exclusive | Equal | Complementary);
private void handleDNFAnd(OperatorPlan plan, Operator and)
throws FrontendException {
// Of N^2 complexity: the reason to limit the DNF size
// process leaves of an AND in DNF tree to remove implicated subexpressions from others
List<Operator> children = plan.getSuccessors(and);
if (children == null)
return;
byte relation;
int size = children.size();
for (int i = 0; i < size; i++) {
if (children.get(i) instanceof ConstantExpression && ((Boolean) ((ConstantExpression) children.get(i)).getValue()))
decrDNFSplitCount((LogicalExpression) children.get(i));
}
for (int i = 0; i < size; i++) {
LogicalExpression child1 = (LogicalExpression) children.get(i);
for (int j = i + 1; j < size; j++) {
LogicalExpression child2 = (LogicalExpression) children.get(j);
relation = inferRelationship(
child1 instanceof LogicalExpressionProxy ? ((LogicalExpressionProxy) child1).src
: child1,
child2 instanceof LogicalExpressionProxy ? ((LogicalExpressionProxy) child2).src
: child2);
if ((relation & Unknown) != 0) {
// no-op
}
else if ((relation & Equal) != 0) {
if (getSplitCount(child1) < getSplitCount(child2) && getSplitCount(child1) > 0) {
if (getSplitCount(child1) > 0)
decrDNFSplitCount(child1);
}
else {
if (getSplitCount(child2) > 0)
decrDNFSplitCount(child2);
}
}
else if ((relation & Exclusive) != 0) {
// no-op now: in the future may desire to replace with a rudimentary false expression
}
else if ((relation & ImplyLeft) != 0) {
if (getSplitCount(child1) > 0)
decrDNFSplitCount(child1);
}
else if ((relation & ImplyRight) != 0) {
if (getSplitCount(child2) > 0)
decrDNFSplitCount(child2);
}
}
}
cleanupDNFPlan(plan, and);
}
private void handleDNFOr(OperatorPlan plan, Operator or)
throws FrontendException {
// Of N^2 complexity: the reason to limit the DNF size
// process children of an OR in DNF tree to remove implicated subexpressions from others
Operator[] children = plan.getSuccessors(or).toArray(
new Operator[0]);
int size = children.length;
for (int i = 0; i < size; i++) {
if (children[i] instanceof ConstantExpression && !((Boolean) ((ConstantExpression) children[i]).getValue()))
decrDNFSplitCount((LogicalExpression) children[i]);
}
for (int ii = 0; ii < size; ii++) {
LogicalExpression child = (LogicalExpression) children[ii];
if (child instanceof AndExpression || (child instanceof DNFExpression && ((DNFExpression) child).type == DNFExpression.DNFExpressionType.AND)) {
handleDNFAnd(plan, child);
}
}
byte relation;
children = plan.getSuccessors(or).toArray(new Operator[0]);
size = children.length;
for (int i = 0; i < size; i++) {
LogicalExpression child1 = (LogicalExpression) children[i];
boolean proxy1 = (child1 instanceof LogicalExpressionProxy);
for (int j = i + 1; j < size; j++) {
LogicalExpression child2 = (LogicalExpression) children[j];
boolean proxy2 = child2 instanceof LogicalExpressionProxy;
relation = inferRelationship(
proxy1 ? ((LogicalExpressionProxy) child1).src
: child1,
proxy2 ? ((LogicalExpressionProxy) child2).src
: child2);
if ((relation & Unknown) != 0) {
// no-op
}
else if ((relation & Equal) != 0) {
if (getSplitCount(child1) < getSplitCount(child2) && getSplitCount(child1) > 0) {
if (getSplitCount(child1) > 0)
decrDNFSplitCount(child1);
}
else {
if (getSplitCount(child2) > 0)
decrDNFSplitCount(child2);
}
}
else if ((relation & ImplyLeft) != 0) {
if (getSplitCount(child2) > 0)
decrDNFSplitCount(child2);
}
else if ((relation & ImplyRight) != 0) {
if (getSplitCount(child1) > 0)
decrDNFSplitCount(child1);
}
else if ((relation & Complementary) != 0) {
if (getSplitCount(child1) > 0)
decrDNFSplitCount(child1);
decrDNFSplitCount(child2);
}
else if ((relation & Exclusive) != 0) {
// no-op
}
}
}
cleanupDNFPlan(plan, or);
}
private void removeDescendants(OperatorPlan plan, Operator op)
throws FrontendException {
// remove recursively a operator and it descendants from the plan
if (plan.getSuccessors(op) == null) return;
Object[] children = plan.getSuccessors(op).toArray();
if (children != null) {
for (Object c : children) {
Operator child = (Operator) c;
removeDescendants(plan, child);
plan.disconnect(op, child);
if (child instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) child).decrSrcDNFSplitCounter();
else decrDNFSplitCount((LogicalExpression) child);
plan.remove(child);
}
}
}
private void cleanupDNFPlan(OperatorPlan plan, Operator root)
throws FrontendException {
// clean up the DNF subtree rooted at 'root'
Object[] children = plan.getSuccessors(root).toArray();
for (Object c : children) {
LogicalExpression child = (LogicalExpression) c;
if (dnfTrimmed(child)) {
removeDescendants(plan, child);
plan.disconnect(root, child);
if (child instanceof LogicalExpressionProxy)
((LogicalExpressionProxy) child).decrSrcDNFSplitCounter();
plan.remove(child);
}
}
if (plan.getSuccessors(root) != null && plan.getSuccessors(root).size() == 1) {
Operator child = plan.getSuccessors(root).get(0);
plan.disconnect(root, child);
if (plan.getPredecessors(root) != null) {
Operator[] preds = plan.getPredecessors(root).toArray(
new Operator[0]);
for (Operator pred : preds) {
plan.disconnect(pred, root);
plan.connect(pred, child);
}
}
if (root instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) root).decrSrcDNFSplitCounter();
else decrDNFSplitCount((LogicalExpression) root);
plan.remove(root);
}
else if (plan.getSuccessors(root) == null) {
plan.remove(root);
}
}
private byte inferRelationship(LogicalExpression e1,
LogicalExpression e2) throws FrontendException {
// process DNF subexpressions: both could be AND of Leaves, or LEAF
byte result = 0;
if (e1.isEqual(e2)) {
result = Equal | ImplyLeft | ImplyRight;
return result;
}
boolean and1 = (e1 instanceof AndExpression || (e1 instanceof DNFExpression && ((DNFExpression) e1).type == DNFExpression.DNFExpressionType.AND));
boolean and2 = (e2 instanceof AndExpression || (e2 instanceof DNFExpression && ((DNFExpression) e2).type == DNFExpression.DNFExpressionType.AND));
if (e1 instanceof NotExpression && e2 instanceof IsNullExpression) {
return handleNot((NotExpression) e1, (IsNullExpression) e2);
}
else if (e2 instanceof NotExpression && e1 instanceof IsNullExpression) {
return switchImplicationSides(handleNot((NotExpression) e2,
(IsNullExpression) e1));
}
else if (and1 && !and2) {
return handleAndSimple(e1, e2);
}
else if (and2 && !and1) {
return switchImplicationSides(handleAndSimple(e2, e1));
}
else if (and1 && and2) {
return handleAnd(e1, e2);
}
else if (e1 instanceof BinaryExpression && e2 instanceof BinaryExpression) {
return handleBinary(e1, e2);
}
else return Unknown;
}
private byte switchImplicationSides(byte ori) {
byte result = ori;
result &= ~(ImplyLeft | ImplyRight);
if ((ori & ImplyLeft) != 0) result |= ImplyRight;
if ((ori & ImplyRight) != 0) result |= ImplyLeft;
return result;
}
private byte handleAnd(LogicalExpression e1, LogicalExpression e2)
throws FrontendException {
// get the inference relation between two AND expressions
List<Operator> children1 = e1.getPlan().getSuccessors(e1), children2 = e2.getPlan().getSuccessors(
e2);
byte result = 0;
// knownFlags is used to track which two children from lhs and rhs has inferrable relationships
// The purpose is to avoid calling handleBinary more than necessary
// The scenario is that is one side is inferrable from the other, then none of this side's children
// should have un-inferrable relationship from the other although the other side's children could
// have un-inferrable relationship with this side's. An example is "C1 > 3" is inferrable from
// "C1 > 5 AND C2 != null", although "C1 > 3" has no relationship with "C2 != null".
boolean[][] knownFlags = new boolean[children1.size()][children2.size()];
boolean[][] equalFlags = new boolean[children1.size()][children2.size()];
for (int i = 0; i < children1.size(); i++) {
for (int j = 0; j < children2.size(); j++) {
byte inferResult = handleBinary(
(LogicalExpression) children1.get(i),
(LogicalExpression) children2.get(j));
if ((inferResult & Unknown) != 0) {
knownFlags[i][j] = false;
equalFlags[i][j] = false;
}
else {
knownFlags[i][j] = true;
result |= inferResult;
if ((inferResult & Equal) != 0)
equalFlags[i][j] = true;
}
}
}
if ((result & Exclusive) != 0) return Exclusive;
if ((result & ImplyRight) != 0 && (result & ImplyLeft) == 0) {
boolean allUnknown = true;
for (int j = 0; j < children2.size(); j++) {
allUnknown = true;
for (int i = 0; i < children1.size(); i++) {
if (knownFlags[i][j]) {
allUnknown = false;
break;
}
}
if (allUnknown) break;
}
if (!allUnknown) result |= ImplyRight;
}
if ((result & ImplyLeft) != 0 && (result & ImplyRight) == 0) {
boolean allUnknown = true;
for (int i = 0; i < children1.size(); i++) {
allUnknown = true;
for (int j = 0; j < children2.size(); j++) {
if (knownFlags[i][j]) {
allUnknown = false;
break;
}
}
if (allUnknown) break;
}
if (!allUnknown) result |= ImplyRight;
}
if ((result & ImplyRight) != 0 && (result & ImplyLeft) != 0) {
// only if all children of either one AND expr have equal counterpart in the other's children
// can the two AND exprs be declared as equal
boolean allEqual = true;
for (int i = 0; i < children1.size(); i++) {
allEqual = true;
for (int j = 0; j < children2.size(); j++) {
if (!equalFlags[i][j]) {
allEqual = false;
break;
}
}
if (!allEqual) break;
}
if (allEqual) {
for (int j = 0; j < children1.size(); j++) {
allEqual = true;
for (int i = 0; i < children2.size(); i++) {
if (!equalFlags[i][j]) {
allEqual = false;
break;
}
}
if (!allEqual) break;
}
}
if (allEqual) result |= Equal;
else result = Unknown;
}
if (result == 0) return Unknown;
else return result;
}
private byte handleAndSimple(LogicalExpression e1, LogicalExpression e2)
throws FrontendException {
if (e2 instanceof ConstantExpression)
// no relationship between an AND and a constant
return Unknown;
// get the inference relation between e1, an AND expression, and e2, a leaf logical expression
List<Operator> andChildren = e1.getPlan().getSuccessors(e1);
boolean hasUnknown = false;
byte result = 0;
int size = andChildren.size();
for (int i = 0; i < size; i++) {
byte inferResult = handleBinary(
(LogicalExpression) andChildren.get(i), e2);
if (!hasUnknown && (inferResult & Unknown) != 0) hasUnknown = true;
else {
result |= inferResult;
}
}
if ((result & Exclusive) != 0) return Exclusive;
else if (hasUnknown) {
if ((result & ImplyRight) != 0) return ImplyRight;
else return Unknown;
}
else {
if ((result & ImplyRight) != 0) return ImplyRight;
else if (result == ImplyLeft) return ImplyLeft;
else return Unknown;
}
}
private byte handleNot(NotExpression not, IsNullExpression isnull)
throws FrontendException {
if (not.getExpression().isEqual(isnull)) return Complementary | Exclusive;
else return Unknown;
}
private byte handleBinary(LogicalExpression e1, LogicalExpression e2)
throws FrontendException {
boolean proxy1 = e1 instanceof LogicalExpressionProxy, proxy2 = e2 instanceof LogicalExpressionProxy;
LogicalExpression le1 = proxy1 ? ((LogicalExpressionProxy) e1).src
: e1, le2 = proxy2 ? ((LogicalExpressionProxy) e2).src
: e2;
if (le1 instanceof NotExpression || le1 instanceof IsNullExpression || le2 instanceof NotExpression || le2 instanceof IsNullExpression)
{
if (((le1 instanceof NotExpression && ((NotExpression)le1).getExpression() instanceof IsNullExpression) &&
le2 instanceof IsNullExpression) ||
((le2 instanceof NotExpression && ((NotExpression)le2).getExpression() instanceof IsNullExpression) &&
le1 instanceof IsNullExpression))
return Exclusive;
else
return Unknown;
}
if (!(le1 instanceof BinaryExpression)||!(le2 instanceof BinaryExpression))
return Unknown;
BinaryExpression b1 = !proxy1 ? (BinaryExpression) e1
: (BinaryExpression) ((LogicalExpressionProxy) e1).src, b2 = !proxy2 ? (BinaryExpression) e2
: (BinaryExpression) ((LogicalExpressionProxy) e2).src;
LogicalExpression l1 = b1.getLhs(), r1 = b1.getRhs(), l2 = b2.getLhs(), r2 = b2.getRhs();
if (l1 instanceof ConstantExpression && l2 instanceof ConstantExpression && r1.isEqual(r2)) {
return handleComparison(
l1,
r1,
l2,
r2,
proxy1 ? ((LogicalExpressionProxy) e1).src : e1,
proxy2 ? ((LogicalExpressionProxy) e2).src : e2);
}
else if (r1 instanceof ConstantExpression && l2 instanceof ConstantExpression && l1.isEqual(r2)) {
return handleComparison(
r1,
l1,
l2,
r2,
proxy1 ? ((LogicalExpressionProxy) e1).src : e1,
proxy2 ? ((LogicalExpressionProxy) e2).src : e2);
}
else if (l1 instanceof ConstantExpression && r2 instanceof ConstantExpression && r1.isEqual(l2)) {
return handleComparison(
l1,
r1,
r2,
l2,
proxy1 ? ((LogicalExpressionProxy) e1).src : e1,
proxy2 ? ((LogicalExpressionProxy) e2).src : e2);
}
else if (r1 instanceof ConstantExpression && r2 instanceof ConstantExpression && l1.isEqual(l2)) {
return handleComparison(
r1,
l1,
r2,
l2,
proxy1 ? ((LogicalExpressionProxy) e1).src : e1,
proxy2 ? ((LogicalExpressionProxy) e2).src : e2);
}
else return Unknown;
}
@SuppressWarnings("unchecked")
private byte handleComparison(LogicalExpression val1,
LogicalExpression k1, LogicalExpression val2,
LogicalExpression k2, LogicalExpression e1,
LogicalExpression e2) {
Object v1 = ((ConstantExpression) val1).getValue(), v2 = ((ConstantExpression) val2).getValue();
boolean comparable1 = v1 instanceof Comparable, comparable2 = v2 instanceof Comparable;
boolean isEqual1 = e1 instanceof EqualExpression, isEqual2 = e2 instanceof EqualExpression, isNotEqual1 = e1 instanceof NotEqualExpression, isNotEqual2 = e2 instanceof NotEqualExpression, isGT1 = e1 instanceof GreaterThanExpression, isGT2 = e2 instanceof GreaterThanExpression, isGE1 = e1 instanceof GreaterThanEqualExpression, isGE2 = e2 instanceof GreaterThanEqualExpression, isLT1 = e1 instanceof LessThanExpression, isLT2 = e2 instanceof LessThanExpression, isLE1 = e1 instanceof LessThanEqualExpression, isLE2 = e2 instanceof LessThanEqualExpression;
if (isEqual1 && isEqual2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
else return Exclusive;
}
else if (isEqual1 && isNotEqual2) {
if (v1.equals(v2)) return Exclusive;
else return ImplyRight;
}
else if (isNotEqual1 && isEqual2) {
if (v1.equals(v2)) return Exclusive;
else return ImplyLeft;
}
else if (isNotEqual1 && isNotEqual2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
else return Unknown;
}
else if (isEqual1 && isGT2) {
if (v1.equals(v2)) return Exclusive;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyRight;
else return Exclusive;
}
else return Unknown;
}
else if (isEqual1 && isGE2) {
if (v1.equals(v2)) return ImplyRight;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyRight;
else return Exclusive;
}
else return Unknown;
}
else if (isEqual1 && isLT2) {
if (v1.equals(v2)) return Exclusive;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return Exclusive;
else return ImplyRight;
}
else return Unknown;
}
else if (isNotEqual1 && isGT2) {
if (v1.equals(v2)) return ImplyLeft;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyLeft;
else return Unknown;
}
else return Unknown;
}
else if (isNotEqual1 && isGE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyLeft;
else return Unknown;
}
else return Unknown;
}
else if (isNotEqual1 && isLT2) {
if (v1.equals(v2)) return ImplyLeft;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Unknown;
else return ImplyLeft;
}
else return Unknown;
}
else if (isNotEqual1 && isLE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (comparable1) {
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Unknown;
else return ImplyLeft;
}
else return Unknown;
}
else if (isGT1 && isGT2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyLeft;
else return ImplyRight;
}
else if (isGT1 && isGE2) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyLeft;
else return ImplyRight;
}
else if (isGT1 && isLT2) {
if (v1.equals(v2)) return Unknown;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Unknown;
else return Exclusive;
}
else if (isGT1 && isLE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Unknown;
else return Exclusive;
}
else if (isGE1 && isGT2) {
if (v1.equals(v2)) return ImplyLeft;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyRight;
else return ImplyLeft;
}
else if (isGE1 && isGE2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyRight;
else return ImplyLeft;
}
else if (isGE1 && isLT2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return Exclusive;
else return Unknown;
}
else if (isGE1 && isLE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return Exclusive;
else return Unknown;
}
else if (isLT1 && isGT2) {
if (v1.equals(v2)) return Unknown;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Exclusive;
else return Unknown;
}
else if (isLT1 && isGE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Exclusive;
else return Unknown;
}
else if (isLT1 && isLT2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyRight;
else return ImplyLeft;
}
else if (isLT1 && isLE2) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return ImplyRight;
else return ImplyLeft;
}
else if (isLE1 && isGT2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return Unknown;
else return Exclusive;
}
else if (isLE1 && isGE2) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v1).compareTo((Comparable) v2) < 0) return Exclusive;
else return Unknown;
}
else if (isLE1 && isLT2) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyLeft;
else return ImplyRight;
}
else if (isLE1 && isLE2) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v1).compareTo((Comparable) v2) > 0) return ImplyLeft;
else return ImplyRight;
}
else if (isNotEqual2 && isGT1) {
if (v1.equals(v2)) return ImplyRight;
if (comparable2) {
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyRight;
else return Unknown;
}
else return Unknown;
}
else if (isNotEqual2 && isGE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (comparable2) {
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyRight;
else return Unknown;
}
else return Unknown;
}
else if (isNotEqual2 && isLT1) {
if (v1.equals(v2)) return ImplyRight;
if (comparable2) {
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Unknown;
else return ImplyRight;
}
else return Unknown;
}
else if (isNotEqual2 && isLE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (comparable2) {
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Unknown;
else return ImplyRight;
}
else return Unknown;
}
else if (isGT2 && isGT1) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyRight;
else return Unknown;
}
else if (isGT2 && isGE1) {
if (v1.equals(v2)) return ImplyLeft;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyRight;
else return ImplyLeft;
}
else if (isGT2 && isLT1) {
if (v1.equals(v2)) return Unknown;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Unknown;
else return Exclusive;
}
else if (isGT2 && isLE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Unknown;
else return Exclusive;
}
else if (isGE2 && isGT1) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return ImplyLeft;
else return ImplyRight;
}
else if (isGE2 && isGE1) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return ImplyLeft;
else return ImplyRight;
}
else if (isGE2 && isLT1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return Exclusive;
else return Unknown;
}
else if (isGE2 && isLE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return Exclusive;
else return Unknown;
}
else if (isLT2 && isGT1) {
if (v1.equals(v2)) return Unknown;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Exclusive;
else return Unknown;
}
else if (isLT2 && isGE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Exclusive;
else return Unknown;
}
else if (isLT2 && isLT1) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyLeft;
else return ImplyRight;
}
else if (isLT2 && isLE1) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return ImplyLeft;
else return ImplyRight;
}
else if (isLE2 && isGT1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return Unknown;
else return Exclusive;
}
else if (isLE2 && isGE1) {
if (v1.equals(v2)) return Complementary | Exclusive;
if (((Comparable) v2).compareTo((Comparable) v1) < 0) return Exclusive;
else return Unknown;
}
else if (isLE2 && isLT1) {
if (v1.equals(v2)) return ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return ImplyRight;
else return ImplyLeft;
}
else if (isLE2 && isLE1) {
if (v1.equals(v2)) return Equal | ImplyLeft | ImplyRight;
if (((Comparable) v2).compareTo((Comparable) v1) > 0) return ImplyRight;
else return ImplyLeft;
}
return Unknown;
}
private void trimLogicalExpressionPlan(OperatorPlan ori)
throws FrontendException {
class TrimVisitor extends AllSameExpressionVisitor {
LogicalExpressionPlan plan;
TrimVisitor(LogicalExpressionPlan plan)
throws FrontendException {
super(plan, new ReverseDependencyOrderWalker(plan));
// the plan will be trimmed in-place on the original plan: this is
// ok because the ReverseDependencyOrderWalker first build the
// traversal ordering from the original plan, then starts the
// traversal which does not rely upon the topology of the original plan
this.plan = plan;
}
@Override
public void execute(LogicalExpression e)
throws FrontendException {
if (dnfTrimmed(e)) {
remove(e);
}
}
@Override
public void visit(NotExpression op) throws FrontendException {
if (op.getExpression() == null) {
remove(op);
}
else execute(op);
}
private void remove(Operator op) throws FrontendException {
List<Operator> p = plan.getPredecessors(op);
if (p != null) {
Operator[] preds = p.toArray(new Operator[0]);
for (Operator pred : preds) {
plan.disconnect(pred, op);
}
}
removeDescendants(op);
}
private void removeDescendants(Operator op)
throws FrontendException {
List<Operator> p = plan.getSuccessors(op);
if (p != null) {
Operator[] sucs = p.toArray(new Operator[0]);
for (Operator suc : sucs) {
plan.disconnect(op, suc);
remove(suc);
}
}
plan.remove(op);
}
@Override
public void visit(OrExpression orExpr) throws FrontendException {
List<Operator> children = plan.getSuccessors(orExpr);
Operator lhs = children != null && children.size() > 0 ? children.get(0)
: null, rhs = children != null && children.size() > 1 ? children.get(1)
: null;
if ((lhs == null && rhs == null) || dnfTrimmed(orExpr)) {
remove(orExpr);
}
else if (rhs == null) {
trimOneChild(orExpr, lhs);
plan.remove(orExpr);
}
}
@Override
public void visit(AndExpression andExpr)
throws FrontendException {
List<Operator> children = plan.getSuccessors(andExpr);
Operator lhs = children != null && children.size() > 0 ? children.get(0)
: null, rhs = children != null && children.size() > 1 ? children.get(1)
: null;
if ((lhs == null && rhs == null) || dnfTrimmed(andExpr)) {
remove(andExpr);
}
else if (rhs == null) {
trimOneChild(andExpr, lhs);
plan.remove(andExpr);
}
}
private void trimOneChild(Operator parent,
Operator survivingChild)
throws FrontendException {
plan.disconnect(parent, survivingChild);
if (plan.getPredecessors(parent) == null) return;
Operator[] preds = plan.getPredecessors(parent).toArray(
new Operator[0]);
for (Operator pred : preds) {
// keep the relative position
Pair<Integer, Integer> pos = plan.disconnect(pred, parent);
plan.connect(pred, pos.first, survivingChild, pos.second);
}
}
}
TrimVisitor worker = new TrimVisitor((LogicalExpressionPlan) ori);
worker.visit();
}
@Override
public OperatorPlan reportChanges() {
return plan;
}
}
@Override
protected OperatorPlan buildPattern() {
LogicalPlan plan = new LogicalPlan();
LogicalRelationalOperator op = new LOFilter(plan);
plan.add(op);
return plan;
}
}