/*
* 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.test.utils.planComparer;
import org.apache.pig.impl.logicalLayer.LogicalPlan;
import org.apache.pig.impl.logicalLayer.LogicalOperator;
import org.apache.pig.impl.plan.OperatorKey;
import org.apache.pig.impl.logicalLayer.FrontendException;
import org.apache.pig.impl.logicalLayer.schema.Schema;
import org.apache.pig.data.DataType;
import org.apache.pig.impl.logicalLayer.FrontendException;
import java.util.Iterator;
/***
* This class is used for LogicalPlan comparison based on:-
* - Graph connectivity
* - Schema of each operator
*
* Extend this class for adding extra comparison features.
*/
public class LogicalPlanComparer
extends PlanStructuralComparer<LogicalOperator, LogicalPlan> {
/***
* This method does a structural comparison of two plans.
*
* @param plan1
* @param plan2
* @param messages
* @return
*/
@Override
public boolean structurallyEquals(LogicalPlan plan1,
LogicalPlan plan2,
StringBuilder messages) {
// Stage 1: Compare connectivity
if (!super.structurallyEquals(plan1, plan2, messages)) {
return false ;
}
// Stage 2: Compare node types
if (findMismatchNodeType(plan1, plan2, messages) > 0) {
return false ;
}
// Stage 3: Compare schemas
if (findMismatchSchema(plan1, plan2, messages)) {
return false ;
}
return true ;
}
/***
* Compare schemas of the same logical operator on different plans
*
* @param plan1
* @param plan2
* @param messages
* @return
*/
private boolean findMismatchSchema(LogicalPlan plan1, LogicalPlan plan2, StringBuilder messages) {
Iterator<OperatorKey> keyIter = plan1.getKeys().keySet().iterator() ;
// for each Logical Operator pair, we compare schema
while(keyIter.hasNext()) {
OperatorKey key = keyIter.next() ;
LogicalOperator op1 = plan1.getOperator(key) ;
LogicalOperator op2 = plan2.getOperator(plan1ToPlan2.get(key)) ;
Schema schema1 = null ;
Schema schema2 = null ;
try {
schema1 = op1.getSchema() ;
schema2 = op2.getSchema() ;
}
catch(FrontendException fe) {
throw new RuntimeException("Cannot get schema from logical plan") ;
}
if (!Schema.equals(schema1, schema2, false, false)) {
messages.append("Schema mismatch ") ;
messages.append(op1.getClass().getSimpleName()) ;
appendOpKey(op1.getOperatorKey(), messages) ;
StringBuilder schemaStr1 = new StringBuilder() ;
StringBuilder schemaStr2 = new StringBuilder() ;
try {
Schema.stringifySchema(schemaStr1 ,schema1, DataType.BAG) ;
Schema.stringifySchema(schemaStr2 ,schema2, DataType.BAG) ;
} catch (FrontendException fee) {
throw new RuntimeException("Cannot stringify schema") ;
}
messages.append(":") ;
messages.append(schemaStr1.toString()) ;
messages.append(" vs ") ;
messages.append(schemaStr2.toString()) ;
messages.append("\n") ;
return true;
}
}
return false;
}
/***
* Find type mismatch between vertices in two plans
* with the same key.
* This method assumes the key sets from two plans are the same.
* @param plan1
* @param plan2
* @param messages
* @return
*/
private int findMismatchNodeType(LogicalPlan plan1,
LogicalPlan plan2,
StringBuilder messages) {
int diffCount = 0 ;
Iterator<OperatorKey> keyIter1 = plan1.getKeys().keySet().iterator() ;
// for each vertex, find diff for edges
while(keyIter1.hasNext()) {
OperatorKey opKey = keyIter1.next() ;
LogicalOperator op1 = plan1.getOperator(opKey) ;
LogicalOperator op2 = plan2.getOperator(plan1ToPlan2.get(opKey)) ;
if (op1.getClass() != op2.getClass()) {
if (messages != null) {
messages.append("Mismatch type:") ;
appendOpKey(opKey, messages) ;
messages.append(" ") ;
messages.append(op1.getClass().getSimpleName()) ;
messages.append(" vs ") ;
messages.append(op2.getClass().getSimpleName()) ;
messages.append("\n") ;
}
diffCount++ ;
}
}
return diffCount ;
}
}