/** * 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.Deque; import java.util.LinkedList; import org.apache.pig.impl.logicalLayer.FrontendException; import org.apache.pig.newplan.logical.expression.*; import org.apache.pig.newplan.ReverseDependencyOrderWalker; import org.apache.pig.newplan.Operator; import org.apache.pig.newplan.OperatorPlan; /** * * A utility class to generate a DNF plan from a base plan. * Not that DNF plan can be generated only AFTER all NOTs * are pushed below the OR/AND expression, namely, the * NOTConversionVisitor should be applied before DNF can be * generated. * */ class DNFPlanGenerator extends LogicalExpressionVisitor { private OperatorPlan dnfPlan = null; Deque<LogicalExpression> result; DNFPlanGenerator(OperatorPlan plan) throws FrontendException { super(plan, new ReverseDependencyOrderWalker(plan)); result = new LinkedList<LogicalExpression>(); } OperatorPlan getDNFPlan() { if (dnfPlan == null) dnfPlan = (result.isEmpty() ? plan : result.pop().getPlan()); return dnfPlan; } @Override public void visit(AndExpression exp) throws FrontendException { LogicalExpression rhsExp = ((exp.getRhs() instanceof AndExpression || exp.getRhs() instanceof OrExpression ? result.pop() : exp.getRhs())); LogicalExpression lhsExp = ((exp.getLhs() instanceof AndExpression || exp.getLhs() instanceof OrExpression ? result.pop() : exp.getLhs())); if (!(lhsExp instanceof AndExpression) && !(lhsExp instanceof DNFExpression) && !(lhsExp instanceof OrExpression) && !(rhsExp instanceof AndExpression) && !(rhsExp instanceof OrExpression) && !(rhsExp instanceof DNFExpression)) result.push(exp); else { if (dnfPlan == null) dnfPlan = new DNFPlan(); boolean isLhsAnd = lhsExp instanceof AndExpression || (lhsExp instanceof DNFExpression && ((DNFExpression) lhsExp).type == DNFExpression.DNFExpressionType.AND), isRhsAnd = rhsExp instanceof AndExpression || (rhsExp instanceof DNFExpression && ((DNFExpression) rhsExp).type == DNFExpression.DNFExpressionType.AND); LogicalExpression current; if (isLhsAnd && isRhsAnd) { current = new DNFExpression("dnfAnd", dnfPlan, DNFExpression.DNFExpressionType.AND); ((DNFPlan) dnfPlan).safeAdd(current); result.push(current); addChildren(current, lhsExp); addChildren(current, rhsExp); } else { boolean isLhsOr = lhsExp instanceof OrExpression || (lhsExp instanceof DNFExpression && ((DNFExpression) lhsExp).type == DNFExpression.DNFExpressionType.OR), isRhsOr = rhsExp instanceof OrExpression || (rhsExp instanceof DNFExpression && ((DNFExpression) rhsExp).type == DNFExpression.DNFExpressionType.OR); if (isLhsOr || isRhsOr) current = new DNFExpression( "dnfOr", dnfPlan, DNFExpression.DNFExpressionType.OR); else current = new DNFExpression("dnfAnd", dnfPlan, DNFExpression.DNFExpressionType.AND); ((DNFPlan) dnfPlan).safeAdd(current); result.push(current); if (!isLhsOr && !isRhsOr) { if (isLhsAnd) addChildren(current, lhsExp); else if (!isLhsOr) { // lhs is a simple expression ((DNFPlan) dnfPlan).safeAdd(lhsExp); dnfPlan.connect(current, lhsExp); } if (isRhsAnd) addChildren(current, rhsExp); else if (!isRhsOr) { // rhs is a simple expression ((DNFPlan) dnfPlan).safeAdd(rhsExp); dnfPlan.connect(current, rhsExp); } } else if (!isLhsOr) { // rhs is OR if (!isLhsAnd) { // lhs is simple mergeSimpleOr(current, lhsExp, rhsExp, true); } else { // lhs is AND mergeAndOr(current, lhsExp, rhsExp, true); } } else if (!isRhsOr) { // lhs is OR if (!isRhsAnd) { // rhs is simple mergeSimpleOr(current, rhsExp, lhsExp, false); } else { // rhs is AND mergeAndOr(current, rhsExp, lhsExp, false); } } else { // both lhs and rhs are OR Operator[] lhsChildren = lhsExp.getPlan().getSuccessors( lhsExp).toArray(new Operator[0]); Operator[] rhsChildren = rhsExp.getPlan().getSuccessors( rhsExp).toArray(new Operator[0]); boolean lhsDNF = lhsExp.getPlan() == dnfPlan, rhsDNF = rhsExp.getPlan() == dnfPlan; int lsize = lhsChildren.length, rsize = rhsChildren.length; LogicalExpression[][] grandChildrenL = new LogicalExpression[lsize][];; for (int i = 0; i < lsize; i++) { if (lhsChildren[i] instanceof AndExpression) { grandChildrenL[i] = lhsChildren[i].getPlan().getSuccessors( lhsChildren[i]).toArray( new LogicalExpression[0]); } else if (lhsChildren[i] instanceof DNFExpression) { grandChildrenL[i] = dnfPlan.getSuccessors( lhsChildren[i]).toArray( new LogicalExpression[0]); } else { grandChildrenL[i] = new LogicalExpression[1]; grandChildrenL[i][0] = (LogicalExpression) lhsChildren[i]; } } LogicalExpression[][] grandChildrenR = new LogicalExpression[rsize][];; for (int i = 0; i < rsize; i++) { if (rhsChildren[i] instanceof AndExpression) { grandChildrenR[i] = rhsChildren[i].getPlan().getSuccessors( rhsChildren[i]).toArray( new LogicalExpression[0]); } else if (rhsChildren[i] instanceof DNFExpression) { grandChildrenR[i] = dnfPlan.getSuccessors( rhsChildren[i]).toArray( new LogicalExpression[0]); } else { grandChildrenR[i] = new LogicalExpression[1]; grandChildrenR[i][0] = (LogicalExpression) rhsChildren[i]; } } if (lhsDNF) { removeDescendants(dnfPlan, lhsExp); dnfPlan.remove(lhsExp); } if (rhsDNF) { removeDescendants(dnfPlan, rhsExp); dnfPlan.remove(rhsExp); } for (int i = 0; i < lsize; i++) for (LogicalExpression lgchild : grandChildrenL[i]) if (lgchild instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) lgchild).restoreSrc(); for (int i = 0; i < rsize; i++) for (LogicalExpression rgchild : grandChildrenR[i]) if (rgchild instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) rgchild).restoreSrc(); for (int i = 0; i < lsize; i++) { for (int j = 0; j < rsize; j++) { LogicalExpression child = new DNFExpression( "dnfAnd", dnfPlan, DNFExpression.DNFExpressionType.AND); ((DNFPlan) dnfPlan).safeAdd(child); dnfPlan.connect(current, child); for (LogicalExpression lgchild : grandChildrenL[i]) { LogicalExpressionProxy lhsClone; if (lgchild instanceof LogicalExpressionProxy) { lhsClone = new LogicalExpressionProxy( dnfPlan, ((LogicalExpressionProxy) lgchild).src); } else { lhsClone = new LogicalExpressionProxy( dnfPlan, lgchild); } dnfPlan.add(lhsClone); dnfPlan.connect(child, lhsClone); } for (LogicalExpression rgchild : grandChildrenR[j]) { LogicalExpressionProxy rhsClone; if (rgchild instanceof LogicalExpressionProxy) { rhsClone = new LogicalExpressionProxy( dnfPlan, ((LogicalExpressionProxy) rgchild).src); } else { rhsClone = new LogicalExpressionProxy( dnfPlan, rgchild); } dnfPlan.add(rhsClone); dnfPlan.connect(child, rhsClone); } } } } } } } 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); plan.remove(child); } } } private void mergeSimpleOr(LogicalExpression current, LogicalExpression simple, LogicalExpression or, boolean simpleFirst) throws FrontendException { Operator[] orChildren = or.getPlan().getSuccessors(or).toArray( new Operator[0]); int size = orChildren.length; LogicalExpression[][] grandChildrenOr = new LogicalExpression[size][];; for (int i = 0; i < size; i++) { if (orChildren[i] instanceof DNFExpression) grandChildrenOr[i] = dnfPlan.getSuccessors( orChildren[i]).toArray( new LogicalExpression[0]); else if (orChildren[i] instanceof AndExpression) grandChildrenOr[i] = orChildren[i].getPlan().getSuccessors( orChildren[i]).toArray( new LogicalExpression[0]); else { grandChildrenOr[i] = new LogicalExpression[1]; grandChildrenOr[i][0] = (LogicalExpression) orChildren[i]; } } boolean simpleDNF = simple.getPlan() == dnfPlan, orDNF = or.getPlan() == dnfPlan; if (simpleDNF) { if (simple instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) simple).restoreSrc(); dnfPlan.remove(simple); } if (orDNF) { removeDescendants(dnfPlan, or); dnfPlan.remove(or); } for (int i = 0; i < size; i++) { LogicalExpression child = new DNFExpression("dnfAnd", dnfPlan, DNFExpression.DNFExpressionType.AND); ((DNFPlan) dnfPlan).safeAdd(child); dnfPlan.connect(current, child); LogicalExpressionProxy simpleClone; if (simple instanceof LogicalExpressionProxy) simpleClone = new LogicalExpressionProxy( dnfPlan, ((LogicalExpressionProxy) simple).src); else simpleClone = new LogicalExpressionProxy(dnfPlan, simple); dnfPlan.add(simpleClone); if (simpleFirst) dnfPlan.connect(child, simpleClone); for (Operator gchild : grandChildrenOr[i]) { LogicalExpression childClone; if (gchild instanceof LogicalExpressionProxy) childClone = (LogicalExpression) gchild; else childClone = new LogicalExpressionProxy(dnfPlan, (LogicalExpression) gchild); dnfPlan.add(childClone); dnfPlan.connect(child, childClone); } if (!simpleFirst) dnfPlan.connect(child, simpleClone); } } private void mergeAndOr(LogicalExpression current, LogicalExpression and, LogicalExpression or, boolean andFirst) throws FrontendException { Operator[] andChildren = and.getPlan().getSuccessors(and).toArray( new Operator[0]); Operator[] orChildren = or.getPlan().getSuccessors(or).toArray( new Operator[0]); int orSize = orChildren.length; int andSize = andChildren.length; boolean andDNF = and.getPlan() == dnfPlan, orDNF = or.getPlan() == dnfPlan; LogicalExpression[][] grandChildrenOr = new LogicalExpression[orSize][];; for (int i = 0; i < orSize; i++) { if (orChildren[i] instanceof DNFExpression) grandChildrenOr[i] = dnfPlan.getSuccessors( orChildren[i]).toArray( new LogicalExpression[0]); else if (orChildren[i] instanceof AndExpression) grandChildrenOr[i] = orChildren[i].getPlan().getSuccessors( orChildren[i]).toArray( new LogicalExpression[0]); else { grandChildrenOr[i] = new LogicalExpression[1]; grandChildrenOr[i][0] = (LogicalExpression) orChildren[i]; } } for (Operator andChild : andChildren) { if (andChild instanceof LogicalExpressionProxy) ((LogicalExpressionProxy) andChild).restoreSrc(); } if (andDNF) { removeDescendants(dnfPlan, and); dnfPlan.remove(and); } if (orDNF) { removeDescendants(dnfPlan, or); dnfPlan.remove(or); } for (int i = 0; i < orSize; i++) { LogicalExpression child = new DNFExpression("dnfAnd", dnfPlan, DNFExpression.DNFExpressionType.AND); ((DNFPlan) dnfPlan).safeAdd(child); if (!andFirst) for (Operator gchild : grandChildrenOr[i]) { dnfPlan.connect(child, gchild); } for (int j = 0; j < andSize; j++) { LogicalExpressionProxy andChildClone; if (andChildren[j] instanceof LogicalExpressionProxy) andChildClone = new LogicalExpressionProxy( dnfPlan, ((LogicalExpressionProxy) andChildren[j]).src); else andChildClone = new LogicalExpressionProxy( dnfPlan, (LogicalExpression) andChildren[j]); dnfPlan.connect(child, andChildClone); } if (andFirst) for (Operator gchild : grandChildrenOr[i]) { dnfPlan.connect(child, gchild); } dnfPlan.connect(current, child); } } @Override public void visit(OrExpression exp) throws FrontendException { LogicalExpression rhsExp = ((exp.getRhs() instanceof AndExpression || exp.getRhs() instanceof OrExpression ? result.pop() : exp.getRhs())); LogicalExpression lhsExp = ((exp.getLhs() instanceof AndExpression || exp.getLhs() instanceof OrExpression ? result.pop() : exp.getLhs())); if (!(lhsExp instanceof OrExpression) && (!(lhsExp instanceof DNFExpression) || ((DNFExpression) lhsExp).type == DNFExpression.DNFExpressionType.AND) && !(rhsExp instanceof OrExpression) && (!(rhsExp instanceof DNFExpression) || ((DNFExpression) rhsExp).type == DNFExpression.DNFExpressionType.AND)) result.push(exp); else { if (dnfPlan == null) dnfPlan = new DNFPlan(); LogicalExpression current = new DNFExpression("dnfOr", dnfPlan, DNFExpression.DNFExpressionType.OR); result.push(current); ((DNFPlan) dnfPlan).safeAdd(current); if (lhsExp instanceof OrExpression || (lhsExp instanceof DNFExpression && ((DNFExpression) lhsExp).type == DNFExpression.DNFExpressionType.OR)) addChildren(current, lhsExp); else dnfPlan.connect(current, lhsExp); if (rhsExp instanceof OrExpression || (rhsExp instanceof DNFExpression && ((DNFExpression) rhsExp).type == DNFExpression.DNFExpressionType.OR)) addChildren(current, rhsExp); else dnfPlan.connect(current, rhsExp); } } private void addChildren(LogicalExpression current, LogicalExpression exp) throws FrontendException { OperatorPlan childPlan = exp.getPlan(); Operator[] children = childPlan.getSuccessors(exp).toArray( new Operator[0]); int size = children.length; for (int i = 0; i < size; ++i) { ((DNFPlan) dnfPlan).safeAdd(children[i]); dnfPlan.connect(current, children[i]); } } }