blob: ec86129d190404faaa313debc89d654650c67d19 [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.pig.newplan.logical.visitor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.pig.FuncSpec;
import org.apache.pig.LoadCaster;
import org.apache.pig.LoadFunc;
import org.apache.pig.PigException;
import org.apache.pig.data.DataType;
import org.apache.pig.impl.PigContext;
import org.apache.pig.StreamToPig;
import org.apache.pig.impl.builtin.IdentityColumn;
import org.apache.pig.impl.logicalLayer.FrontendException;
import org.apache.pig.impl.plan.VisitorException;
import org.apache.pig.impl.streaming.StreamingCommand;
import org.apache.pig.impl.streaming.StreamingCommand.HandleSpec;
import org.apache.pig.impl.util.MultiMap;
import org.apache.pig.newplan.DependencyOrderWalker;
import org.apache.pig.newplan.Operator;
import org.apache.pig.newplan.OperatorPlan;
import org.apache.pig.newplan.PlanWalker;
import org.apache.pig.newplan.ReverseDependencyOrderWalker;
import org.apache.pig.newplan.logical.expression.BinCondExpression;
import org.apache.pig.newplan.logical.expression.CastExpression;
import org.apache.pig.newplan.logical.expression.ConstantExpression;
import org.apache.pig.newplan.logical.expression.DereferenceExpression;
import org.apache.pig.newplan.logical.expression.LogicalExpression;
import org.apache.pig.newplan.logical.expression.LogicalExpressionPlan;
import org.apache.pig.newplan.logical.expression.LogicalExpressionVisitor;
import org.apache.pig.newplan.logical.expression.MapLookupExpression;
import org.apache.pig.newplan.logical.expression.ProjectExpression;
import org.apache.pig.newplan.logical.expression.ScalarExpression;
import org.apache.pig.newplan.logical.expression.UserFuncExpression;
import org.apache.pig.newplan.logical.relational.LOCogroup;
import org.apache.pig.newplan.logical.relational.LOCross;
import org.apache.pig.newplan.logical.relational.LODistinct;
import org.apache.pig.newplan.logical.relational.LOFilter;
import org.apache.pig.newplan.logical.relational.LOForEach;
import org.apache.pig.newplan.logical.relational.LOGenerate;
import org.apache.pig.newplan.logical.relational.LOInnerLoad;
import org.apache.pig.newplan.logical.relational.LOJoin;
import org.apache.pig.newplan.logical.relational.LOLimit;
import org.apache.pig.newplan.logical.relational.LOLoad;
import org.apache.pig.newplan.logical.relational.LORank;
import org.apache.pig.newplan.logical.relational.LOSort;
import org.apache.pig.newplan.logical.relational.LOSplit;
import org.apache.pig.newplan.logical.relational.LOSplitOutput;
import org.apache.pig.newplan.logical.relational.LOStore;
import org.apache.pig.newplan.logical.relational.LOStream;
import org.apache.pig.newplan.logical.relational.LOUnion;
import org.apache.pig.newplan.logical.relational.LogicalPlan;
import org.apache.pig.newplan.logical.relational.LogicalRelationalNodesVisitor;
import org.apache.pig.newplan.logical.relational.LogicalRelationalOperator;
import org.apache.pig.newplan.logical.relational.LogicalSchema;
import org.apache.pig.newplan.logical.relational.LogicalSchema.LogicalFieldSchema;
/**
* Create mapping between uid and Load FuncSpec when the LogicalExpression
* associated with it is known to hold an unmodified element of data returned
* by the load function.
* This information is used to find the LoadCaster to be used to cast bytearrays
* into other other types.
*/
public class LineageFindRelVisitor extends LogicalRelationalNodesVisitor{
Map<Long, FuncSpec> uid2LoadFuncMap = new HashMap<Long, FuncSpec>();
//if LogicalRelationalOperator is associated with a single load func spec
// then the mapping is stored here
Map<LogicalRelationalOperator, FuncSpec> rel2InputFuncMap =
new HashMap<LogicalRelationalOperator, FuncSpec>();
Map<FuncSpec, Class> func2casterMap = new HashMap<FuncSpec, Class>();
public LineageFindRelVisitor(OperatorPlan plan) throws FrontendException {
super(plan, new DependencyOrderWalker(plan));
}
public Map<Long, FuncSpec> getUid2LoadFuncMap(){
return uid2LoadFuncMap;
}
@Override
public void visit(LOLoad load) throws FrontendException{
LogicalSchema schema = load.getSchema();
setLoadFuncForUids(schema, load.getFileSpec().getFuncSpec());
rel2InputFuncMap.put(load, load.getFileSpec().getFuncSpec());
}
@Override
public void visit(LOStream stream) throws FrontendException{
mapToPredLoadFunc(stream);
StreamingCommand command = ((LOStream)stream).getStreamingCommand();
HandleSpec streamOutputSpec = command.getOutputSpec();
FuncSpec streamLoaderSpec = new FuncSpec(streamOutputSpec.getSpec());
setLoadFuncForUids(stream.getSchema(), streamLoaderSpec);
rel2InputFuncMap.put(stream, streamLoaderSpec);
}
@Override
public void visit(LOInnerLoad innerLoad) throws FrontendException{
LOForEach foreach = innerLoad.getLOForEach();
LogicalRelationalOperator pred =
((LogicalRelationalOperator)foreach.getPlan().getPredecessors(foreach).get(0));
LogicalSchema predSchema = pred.getSchema();
//if this has a schema, the lineage can be tracked using the uid in input schema
if(innerLoad.getSchema() != null){
if(innerLoad.getSchema().size() == 1
&& innerLoad.getSchema().getField(0).type == DataType.BYTEARRAY
&& uid2LoadFuncMap.get(innerLoad.getSchema().getField(0).uid) == null
&& predSchema != null
){
long inpUid = predSchema.getField(innerLoad.getProjection().getColNum()).uid;
if(uid2LoadFuncMap.get(inpUid) != null){
addUidLoadFuncToMap(innerLoad.getSchema().getField(0).uid, uid2LoadFuncMap.get(inpUid));
}
return;
}
}
// associated load func could not be found using uid, use
// the single load func associated with input relation (if any)
if(getAssociatedLoadFunc(pred) != null){
mapRelToPredLoadFunc(innerLoad, pred);
}
}
/**
* map all uids in schema to funcSpec
* @param schema
* @param funcSpec
* @throws VisitorException
*/
private void setLoadFuncForUids(LogicalSchema schema, FuncSpec funcSpec)
throws VisitorException {
if(schema == null){
return;
}
for(LogicalFieldSchema fs : schema.getFields()){
addUidLoadFuncToMap((Long) fs.uid, funcSpec);
setLoadFuncForUids(fs.schema, funcSpec);
}
}
@Override
public void visit(LOFilter filter) throws FrontendException{
mapToPredLoadFunc(filter);
visitExpression(filter.getFilterPlan());
}
/**
* If all predecessors of relOp are associated with same load
* func , then map reOp to it.
*
* @param relOp
* @throws FrontendException
*/
private void mapToPredLoadFunc(LogicalRelationalOperator relOp)
throws FrontendException {
OperatorPlan lp = relOp.getPlan();
List<Operator> preds = lp.getPredecessors(relOp);
if(lp.getPredecessors(relOp) == null ||
lp.getPredecessors(relOp).size() == 0){
// no predecessors , nothing to do
return;
}
FuncSpec loadFuncSpec = getAssociatedLoadFunc((LogicalRelationalOperator) preds.get(0));
if(loadFuncSpec == null){
return;
}
//ensure that all predecessors are mapped to same load func spec
for(int i=1; i<preds.size(); i++){
if(!haveIdenticalCasters(loadFuncSpec,getAssociatedLoadFunc(
(LogicalRelationalOperator) preds.get(i)))){
return;
}
}
rel2InputFuncMap.put(relOp, loadFuncSpec);
}
/**
* Find single load func spec associated with this relation.
* If the relation has schema, all uids in schema should be associated
* with same load func spec. if it does not have schema check the existing
* mapping
* @param relOp
* @return
* @throws FrontendException
*/
private FuncSpec getAssociatedLoadFunc(LogicalRelationalOperator relOp) throws FrontendException {
LogicalSchema schema = relOp.getSchema();
FuncSpec funcSpec = null;
if(schema != null){
if(schema.size() == 0)
return null;
funcSpec = uid2LoadFuncMap.get(schema.getField(0).uid);
if(funcSpec != null) {
for(int i=1; i<schema.size(); i++){
LogicalFieldSchema fs = schema.getField(i);
if(! haveIdenticalCasters(funcSpec,
uid2LoadFuncMap.get(fs.uid))){
//all uid are not associated with same func spec, there is no
// single func spec that represents all the fields
funcSpec = null;
break;
}
}
}
}
if(funcSpec == null){
// If relOp is LOForEach and contains UDF, byte field could come from UDF.
// We don't assume it share the LoadCaster with predecessor
if (relOp instanceof LOForEach) {
UDFFinder udfFinder = new UDFFinder(((LOForEach) relOp).getInnerPlan());
udfFinder.visit();
if (udfFinder.getUDFList().size()!=0)
return null;
}
funcSpec = rel2InputFuncMap.get(relOp);
}
return funcSpec;
}
private void mapRelToPredLoadFunc(LogicalRelationalOperator relOp,
Operator pred) {
if(rel2InputFuncMap.get(pred) != null){
rel2InputFuncMap.put(relOp, rel2InputFuncMap.get(pred));
}
}
private void visitExpression(LogicalExpressionPlan expPlan)
throws FrontendException{
LineageFindExpVisitor findLineageExp =
new LineageFindExpVisitor(expPlan, uid2LoadFuncMap);
findLineageExp.visit();
}
@Override
public void visit(LOCogroup group) throws FrontendException{
mapToPredLoadFunc(group);
List<Operator> inputs = group.getInputs((LogicalPlan)plan);
// list of field schemas of group plans
List<LogicalFieldSchema> groupPlanSchemas = new ArrayList<LogicalFieldSchema>();
MultiMap<Integer, LogicalExpressionPlan> plans = group.getExpressionPlans();
for(LogicalExpressionPlan expPlan : plans.values()){
visitExpression(expPlan);
if(expPlan.getSources().size() != 1){
throw new AssertionError("Group plans should have only one output");
}
groupPlanSchemas.add(((LogicalExpression)expPlan.getSources().get(0)).getFieldSchema());
}
LogicalSchema sch = group.getSchema();
//if the group plans are associated with same load function , associate
//same load fucntion with group column schema
if (getAssociatedLoadFunc(group)!=null) {
addUidLoadFuncToMap(sch.getField(0).uid, rel2InputFuncMap.get(group));
if (sch.getField(0).schema!=null)
setLoadFuncForUids(sch.getField(0).schema, rel2InputFuncMap.get(group));
}
else
mapMatchLoadFuncToUid(sch.getField(0), groupPlanSchemas);
//set the load func spec for the bags in the schema, this helps if
// the input schemas are not set
//group schema has a group column followed by bags corresponding to each
// input
if(sch.size() != inputs.size()+1 ){
throw new AssertionError("cogroup schema size not same as number of inputs");
}
for(int i=1; i < sch.size(); i++){
long uid = sch.getField(i).uid;
LogicalRelationalOperator input = (LogicalRelationalOperator) inputs.get(i-1);
if(getAssociatedLoadFunc(input) != null){
addUidLoadFuncToMap(uid, rel2InputFuncMap.get(input));
}
}
}
@Override
public void visit(LOJoin join) throws FrontendException{
mapToPredLoadFunc(join);
MultiMap<Integer, LogicalExpressionPlan> plans = join.getExpressionPlans();
for(LogicalExpressionPlan expPlan : plans.values()){
visitExpression(expPlan);
}
}
@Override
public void visit(LOForEach fe) throws FrontendException{
mapToPredLoadFunc(fe);
LogicalPlan innerPlan = fe.getInnerPlan();
PlanWalker newWalker = currentWalker.spawnChildWalker(innerPlan);
pushWalker(newWalker);
currentWalker.walk(this);
popWalker();
}
@Override
public void visit(LOGenerate gen) throws FrontendException{
mapToPredLoadFunc(gen);
List<LogicalExpressionPlan> expPlans = gen.getOutputPlans();
for(LogicalExpressionPlan expPlan : expPlans){
visitExpression(expPlan);
}
//associate flatten output to the load func associated with input
//expression of flatten
boolean[] flattens = gen.getFlattenFlags();
for(int i=0; i<flattens.length; i++){
if(flattens[i] == true){
//get the output schema corresponding to this exp plan
gen.getSchema();
if(gen.getOutputPlanSchemas() == null || gen.getOutputPlanSchemas().size() <= i){
return;
}
LogicalSchema sch = gen.getOutputPlanSchemas().get(i);
if(sch == null){
continue;
}
//get the only output exp of the ith plan
LogicalExpression exp =
(LogicalExpression) gen.getOutputPlans().get(i).getSources().get(0);
//get its funcspec and associate it with uid of all fields in the schema
FuncSpec funcSpec = uid2LoadFuncMap.get(exp.getFieldSchema().uid);
for(LogicalFieldSchema fs : sch.getFields()){
addUidLoadFuncToMap(fs.uid, funcSpec);
}
}
}
}
@Override
public void visit(LOSort sort) throws FrontendException{
mapToPredLoadFunc(sort);
List<LogicalExpressionPlan> expPlans = sort.getSortColPlans();
for(LogicalExpressionPlan expPlan : expPlans){
visitExpression(expPlan);
}
}
@Override
public void visit(LORank rank) throws FrontendException{
mapToPredLoadFunc(rank);
List<LogicalExpressionPlan> expPlans = rank.getRankColPlans();
for(LogicalExpressionPlan expPlan : expPlans){
visitExpression(expPlan);
}
}
@Override
public void visit(LODistinct relOp) throws FrontendException{
mapToPredLoadFunc(relOp);
}
@Override
public void visit(LOLimit loLimit) throws FrontendException{
mapToPredLoadFunc(loLimit);
if (loLimit.getLimitPlan() != null)
visitExpression(loLimit.getLimitPlan());
}
@Override
public void visit(LOSplit relOp) throws FrontendException{
mapToPredLoadFunc(relOp);
}
@Override
public void visit(LOStore relOp) throws FrontendException{
mapToPredLoadFunc(relOp);
}
@Override
public void visit(LOCross relOp) throws FrontendException{
mapToPredLoadFunc(relOp);
}
@Override
public void visit(LOUnion relOp) throws FrontendException{
mapToPredLoadFunc(relOp);
// Since the uid changes for Union, add mappings for new uids to funcspec
LogicalSchema schema = relOp.getSchema();
if(schema != null){
// For each output field, checking all the fields being
// union-ed(bundled) together and only set the funcspec when ALL
// of them come from the same caster
//
// A = (i,j)
// B = (i,j)
// C = UNION A, B;
// Checking if A.i and B.i have the same caster.
// Same for A.j and B.j
// A.i and A.j may come from the different casters
for (LogicalFieldSchema logicalFieldSchema : schema.getFields()) {
Set<Long> inputs = relOp.getInputUids(logicalFieldSchema.uid);
if( inputs.size() == 0 ) {
// uid was not changed.
// funcspec should be already set. skipping
continue;
}
FuncSpec prevLoadFuncSpec = null, curLoadFuncSpec = null;
boolean allSameLoader = true;
for(Long inputUid: inputs) {
curLoadFuncSpec = uid2LoadFuncMap.get(inputUid) ;
if( prevLoadFuncSpec != null
&& !haveIdenticalCasters(prevLoadFuncSpec,
curLoadFuncSpec) ) {
allSameLoader = false;
break;
}
prevLoadFuncSpec = curLoadFuncSpec;
}
if( allSameLoader ) {
addUidLoadFuncToMap(logicalFieldSchema.uid,curLoadFuncSpec);
}
}
}
}
@Override
public void visit(LOSplitOutput split) throws FrontendException{
mapToPredLoadFunc(split);
visitExpression(split.getFilterPlan());
//The uid changes across the input and output of a split.
//In this visitor the uids for the fields in output are mapped to ones
// in input based on position. uid2LoadFuncMap is updated with output
// schema uid and load func mapped to corresponding uid in inputschema
LogicalSchema inputSch =
((LogicalRelationalOperator)split.getPlan().getPredecessors(split).get(0)).getSchema();
LogicalSchema outSchema = split.getSchema();
if(inputSch == null && outSchema == null){
return;
}
if(inputSch == null || outSchema == null){
String msg = "Bug: in split only one of input/output schema is null "
+ split;
throw new VisitorException(split, msg,2260, PigException.BUG) ;
}
if(inputSch.size() != outSchema.size()){
String msg = "Bug: input and output schema size of split differ "
+ split;
throw new VisitorException(split, msg,2261, PigException.BUG) ;
}
for(int i=0; i<inputSch.size(); i++){
LogicalFieldSchema inField = inputSch.getField(i);
LogicalFieldSchema outField = outSchema.getField(i);
if(uid2LoadFuncMap.get(inField.uid) != null){
addUidLoadFuncToMap(outField.uid, uid2LoadFuncMap.get(inField.uid));
}
}
}
/**
* Add given uid, loadFuncSpec to mapping
* @param uid
* @param loadFuncSpec
* @throws VisitorException
*/
private void addUidLoadFuncToMap(long uid, FuncSpec loadFuncSpec)
throws VisitorException{
if(loadFuncSpec == null){
return;
}
//ensure that uid always matches to same load func
FuncSpec curFuncSpec = uid2LoadFuncMap.get(uid);
if(curFuncSpec == null){
uid2LoadFuncMap.put(uid, loadFuncSpec);
}else if(! haveIdenticalCasters(curFuncSpec,loadFuncSpec)){
String msg = "Bug: uid mapped to two different load functions : " +
curFuncSpec + " and " + loadFuncSpec;
throw new VisitorException(msg,2262, PigException.BUG) ;
}
}
/**
* if uid in input field schemas or their inner schemas map to same
* load function, then map the new uid in bincond also to same
* load function in uid2LoadFuncMap
* @param outFS
* @param inputFieldSchemas
* @throws VisitorException
*/
void mapMatchLoadFuncToUid(
LogicalFieldSchema outFS,
List<LogicalFieldSchema> inputFieldSchemas) throws VisitorException {
if(inputFieldSchemas.size() == 0){
return;
}
// If schema of any input is null, we skip output schema
for (LogicalFieldSchema fs : inputFieldSchemas) {
if (fs==null)
return;
}
//if same non null load func is associated with all fieldschemas
// asssociate that with the uid of outFS
LogicalFieldSchema inpFS1 = inputFieldSchemas.get(0);
FuncSpec funcSpec1 = uid2LoadFuncMap.get(inpFS1.uid);
boolean allInnerSchemaMatch = false;
if(funcSpec1 != null){
boolean allMatch = true;
allInnerSchemaMatch = true;
for(LogicalFieldSchema fs : inputFieldSchemas){
//check if all func spec match
if(!haveIdenticalCasters(funcSpec1,uid2LoadFuncMap.get(fs.uid))){
allMatch = false;
}
//check if all inner schema match for use later
if(outFS.schema == null || !outFS.schema.isEqual(fs.schema)){
allInnerSchemaMatch = false;
}
}
if(allMatch){
addUidLoadFuncToMap(outFS.uid, funcSpec1);
}
}
//recursively call the function for corresponding files in inner schemas
if(allInnerSchemaMatch){
List<LogicalFieldSchema> outFields = outFS.schema.getFields();
for(int i=0; i<outFields.size(); i++){
List<LogicalFieldSchema> inFsList = new ArrayList<LogicalFieldSchema>();
for(LogicalFieldSchema fs : inputFieldSchemas){
inFsList.add(fs.schema.getField(i));
}
mapMatchLoadFuncToUid(outFields.get(i), inFsList);
}
}
}
/**
* If a input of dereference or map-lookup has associated load function,
* the same load function should be associated with the dereference or map-lookup.
*
*/
class LineageFindExpVisitor extends LogicalExpressionVisitor{
private Map<Long, FuncSpec> uid2LoadFuncMap;
public LineageFindExpVisitor(LogicalExpressionPlan plan,
Map<Long, FuncSpec> uid2LoadFuncMap) throws FrontendException {
super(plan, new ReverseDependencyOrderWalker(plan));
this.uid2LoadFuncMap = uid2LoadFuncMap;
}
@Override
public void visit(ProjectExpression proj) throws FrontendException {
// proj should have the same uid as input, if input has schema.
// if uid does not have associated func spec and input relation
// has null schema or it is inner-load, use the load func associated
// with the relation
LogicalRelationalOperator inputRel = proj.findReferent();
if (proj.getFieldSchema()==null)
return;
long uid = proj.getFieldSchema().uid;
if(uid2LoadFuncMap.get(uid) == null && (inputRel.getSchema() == null || inputRel instanceof LOInnerLoad)){
FuncSpec funcSpec = rel2InputFuncMap.get(inputRel);
if(funcSpec != null){
addUidLoadFuncToMap(uid, funcSpec);
}
}
}
@Override
public void visit(DereferenceExpression deref) throws FrontendException {
updateUidMap(deref, deref.getReferredExpression());
}
@Override
public void visit(MapLookupExpression mapLookup) throws FrontendException {
updateUidMap(mapLookup, mapLookup.getMap());
}
private void updateUidMap(LogicalExpression exp,
LogicalExpression inp) throws FrontendException {
//find input uid and corresponding load FuncSpec
long inpUid = inp.getFieldSchema().uid;
FuncSpec inpLoadFuncSpec = uid2LoadFuncMap.get(inpUid);
addUidLoadFuncToMap(exp.getFieldSchema().uid, inpLoadFuncSpec);
}
@Override
public void visit(BinCondExpression binCond) throws FrontendException{
//if either side is a null constant, we can safely associate
//this bincond with the load func spec on other side
LogicalExpression lhs = binCond.getLhs();
LogicalExpression rhs = binCond.getRhs();
if(getNULLConstantInCast(lhs) != null){
FuncSpec funcSpec = uid2LoadFuncMap.get(rhs.getFieldSchema().uid);
uid2LoadFuncMap.put(binCond.getFieldSchema().uid, funcSpec);
}
else if(getNULLConstantInCast(rhs) != null){
FuncSpec funcSpec = uid2LoadFuncMap.get(lhs.getFieldSchema().uid);
uid2LoadFuncMap.put(binCond.getFieldSchema().uid, funcSpec);
}
else {
List<LogicalFieldSchema> inFieldSchemas = new ArrayList<LogicalFieldSchema>();
inFieldSchemas.add(lhs.getFieldSchema());
inFieldSchemas.add(rhs.getFieldSchema());
mapMatchLoadFuncToUid(
binCond.getFieldSchema(),
inFieldSchemas
);
}
}
@Override
public void visit(ScalarExpression scalarExp) throws FrontendException{
//track lineage to input load function
LogicalRelationalOperator inputRel = (LogicalRelationalOperator)scalarExp.getAttachedLogicalOperator();
LogicalPlan relPlan = (LogicalPlan) inputRel.getPlan();
List<Operator> softPreds = relPlan.getSoftLinkPredecessors(inputRel);
//1st argument is the column number in input, 2nd arg is the input file name
Integer inputColNum = (Integer)((ConstantExpression) scalarExp.getArguments().get(0)).getValue();
String inputFile = (String)((ConstantExpression) scalarExp.getArguments().get(1)).getValue();
long outputUid = scalarExp.getFieldSchema().uid;
boolean foundInput = false; // a variable to do sanity check on num of input relations
//find the input relation, and use it to get lineage
for(Operator softPred : softPreds){
LOStore inputStore = (LOStore) softPred;
if(inputStore.getFileSpec().getFileName().equals(inputFile)){
if(foundInput == true){
throw new FrontendException(
"More than one input found for scalar expression",
2268,
PigException.BUG
);
}
foundInput = true;
//found the store corresponding to this scalar expression
LogicalSchema sch = inputStore.getSchema();
if(sch == null){
//see if there is a load function associated with the store
FuncSpec funcSpec = rel2InputFuncMap.get(inputStore);
addUidLoadFuncToMap(outputUid, funcSpec);
}else{
//find input uid and corresponding load func
LogicalFieldSchema fs = sch.getField(inputColNum);
FuncSpec funcSpec = uid2LoadFuncMap.get(fs.uid);
addUidLoadFuncToMap(outputUid, funcSpec);
}
}
}
if(foundInput == false){
throw new FrontendException(
"No input found for scalar expression",
2269,
PigException.BUG
);
}
}
@Override
public void visit(UserFuncExpression op) throws FrontendException {
if(op.getFuncSpec().getClassName().equals(IdentityColumn.class.getName())) {
// IdentityColumn only expects one arg
FuncSpec funcSpec = uid2LoadFuncMap.get(op.getArguments().get(0).getFieldSchema().uid);
addUidLoadFuncToMap(op.getFieldSchema().uid, funcSpec);
}
}
/**
* if there is a null constant under casts, return it
* @param rel
* @return
* @throws FrontendException
*/
private Object getNULLConstantInCast(LogicalExpression rel) throws FrontendException {
if(rel instanceof CastExpression){
if( ((CastExpression)rel).getExpression() instanceof CastExpression)
return getNULLConstantInCast(((CastExpression)rel).getExpression());
else if( ((CastExpression)rel).getExpression() instanceof ConstantExpression){
ConstantExpression constExp = (ConstantExpression)((CastExpression)rel).getExpression();
if(constExp.getValue() == null)
return constExp;
else
return null;
}
}
return null;
}
}
//Copied from POCast.instantiateFunc
private Class instantiateCaster(FuncSpec funcSpec) throws VisitorException {
if ( funcSpec == null ) {
return null;
}
if( func2casterMap.containsKey(funcSpec) ) {
return func2casterMap.get(funcSpec);
}
LoadCaster caster = null;
Object obj = PigContext.instantiateFuncFromSpec(funcSpec);
try {
if (obj instanceof LoadFunc) {
caster = ((LoadFunc)obj).getLoadCaster();
} else if (obj instanceof StreamToPig) {
caster = ((StreamToPig)obj).getLoadCaster();
} else {
throw new VisitorException("Invalid class type " + funcSpec.getClassName(),
2270, PigException.BUG );
}
} catch (IOException e) {
throw new VisitorException("Invalid class type " + funcSpec.getClassName(),
2270, e );
}
Class retval = (caster == null) ? null : caster.getClass();
func2casterMap.put(funcSpec, retval);
return retval;
}
private boolean haveIdenticalCasters(FuncSpec f1, FuncSpec f2) throws VisitorException{
if( f1 == null || f2 == null ) {
return false;
}
if( f1.equals(f2) ) {
return true;
}
Class caster1 = instantiateCaster(f1);
Class caster2 = instantiateCaster(f2);
if( caster1 == null || caster2 == null ) {
return false;
}
//From PIG-3295
//If the class name for LoadCaster are the same, and LoadCaster only has
//default constructor, then two LoadCasters are considered equal
if( caster1.getCanonicalName().equals(caster2.getCanonicalName()) &&
caster1.getConstructors().length == 1 &&
caster1.getConstructors()[0].getGenericParameterTypes().length == 0 &&
caster2.getConstructors().length == 1 &&
caster2.getConstructors()[0].getGenericParameterTypes().length == 0 ) {
return true;
}
return false;
}
}