/* * Lisensed 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.tajo.plan.serder; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.tajo.OverridableConf; import org.apache.tajo.algebra.WindowSpec.WindowFrameEndBoundType; import org.apache.tajo.algebra.WindowSpec.WindowFrameStartBoundType; import org.apache.tajo.catalog.Column; import org.apache.tajo.catalog.FunctionDesc; import org.apache.tajo.catalog.SortSpec; import org.apache.tajo.catalog.proto.CatalogProtos; import org.apache.tajo.catalog.proto.CatalogProtos.FunctionSignatureProto; import org.apache.tajo.common.TajoDataTypes.DataType; import org.apache.tajo.datum.*; import org.apache.tajo.exception.TajoInternalError; import org.apache.tajo.exception.UndefinedFunctionException; import org.apache.tajo.plan.expr.*; import org.apache.tajo.plan.function.python.PythonScriptEngine; import org.apache.tajo.plan.logical.TableSubQueryNode; import org.apache.tajo.plan.logical.WindowSpec; import org.apache.tajo.plan.serder.PlanProto.WinFunctionEvalSpec; import org.apache.tajo.type.TypeProtobufEncoder; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import static org.apache.tajo.function.FunctionUtil.buildSimpleFunctionSignature; /** * It deserializes a serialized eval tree consisting of a number of EvalNodes. * * {@link EvalNodeSerializer} serializes an eval tree in a postfix traverse order. * So, this class firstly sorts all serialized eval nodes in ascending order of their sequence IDs. Then, * it sequentially restores each serialized node to EvalNode instance. * * @see EvalNodeSerializer */ public class EvalNodeDeserializer { public static EvalNode deserialize(OverridableConf context, EvalContext evalContext, PlanProto.EvalNodeTree tree) { Map<Integer, EvalNode> evalNodeMap = Maps.newHashMap(); // sort serialized eval nodes in an ascending order of their IDs. List<PlanProto.EvalNode> nodeList = Lists.newArrayList(tree.getNodesList()); Collections.sort(nodeList, new Comparator<PlanProto.EvalNode>() { @Override public int compare(PlanProto.EvalNode o1, PlanProto.EvalNode o2) { return o1.getId() - o2.getId(); } }); EvalNode current = null; // The sorted order is the same of a postfix traverse order. // So, it sequentially transforms each serialized node into a EvalNode instance in a postfix order of // the original eval tree. for (PlanProto.EvalNode protoNode : nodeList) { EvalType type = EvalType.valueOf(protoNode.getType().name()); if (EvalType.isUnaryOperator(type)) { PlanProto.UnaryEval unaryProto = protoNode.getUnary(); EvalNode child = evalNodeMap.get(unaryProto.getChildId()); switch (type) { case NOT: current = new NotEval(child); break; case IS_NULL: current = new IsNullEval(unaryProto.getNegative(), child); break; case CAST: current = new CastEval(context, child, TypeProtobufEncoder.decode(unaryProto.getCastingType())); break; case SIGNED: current = new SignedEval(unaryProto.getNegative(), child); break; default: throw new RuntimeException("Unknown EvalType: " + type.name()); } } else if (EvalType.isBinaryOperator(type)) { PlanProto.BinaryEval binProto = protoNode.getBinary(); EvalNode lhs = evalNodeMap.get(binProto.getLhsId()); EvalNode rhs = evalNodeMap.get(binProto.getRhsId()); switch (type) { case IN: current = new InEval(lhs, (ValueSetEval) rhs, binProto.getNegative()); break; case LIKE: { PlanProto.PatternMatchEvalSpec patternMatchProto = protoNode.getPatternMatch(); current = new LikePredicateEval(binProto.getNegative(), lhs, (ConstEval) rhs, patternMatchProto.getCaseSensitive()); break; } case REGEX: { PlanProto.PatternMatchEvalSpec patternMatchProto = protoNode.getPatternMatch(); current = new RegexPredicateEval(binProto.getNegative(), lhs, (ConstEval) rhs, patternMatchProto.getCaseSensitive()); break; } case SIMILAR_TO: { PlanProto.PatternMatchEvalSpec patternMatchProto = protoNode.getPatternMatch(); current = new SimilarToPredicateEval(binProto.getNegative(), lhs, (ConstEval) rhs, patternMatchProto.getCaseSensitive()); break; } default: current = new BinaryEval(type, lhs, rhs); } } else if (type == EvalType.CONST) { PlanProto.ConstEval constProto = protoNode.getConst(); current = new ConstEval(deserialize(constProto.getValue())); } else if (type == EvalType.ROW_CONSTANT) { PlanProto.RowConstEval rowConstProto = protoNode.getRowConst(); Datum[] values = new Datum[rowConstProto.getValuesCount()]; for (int i = 0; i < rowConstProto.getValuesCount(); i++) { values[i] = deserialize(rowConstProto.getValues(i)); } current = new RowConstantEval(values); } else if (type == EvalType.SUBQUERY) { PlanProto.SubqueryEval subqueryProto = protoNode.getSubquery(); TableSubQueryNode subQueryNode = (TableSubQueryNode) LogicalNodeDeserializer.deserialize(context, evalContext, subqueryProto.getSubquery()); current = new SubqueryEval(subQueryNode); } else if (type == EvalType.FIELD) { CatalogProtos.ColumnProto columnProto = protoNode.getField(); current = new FieldEval(new Column(columnProto)); } else if (type == EvalType.BETWEEN) { PlanProto.BetweenEval betweenProto = protoNode.getBetween(); current = new BetweenPredicateEval(betweenProto.getNegative(), betweenProto.getSymmetric(), evalNodeMap.get(betweenProto.getPredicand()), evalNodeMap.get(betweenProto.getBegin()), evalNodeMap.get(betweenProto.getEnd())); } else if (type == EvalType.CASE) { PlanProto.CaseWhenEval caseWhenProto = protoNode.getCasewhen(); CaseWhenEval caseWhenEval = new CaseWhenEval(); for (int i = 0; i < caseWhenProto.getIfCondsCount(); i++) { caseWhenEval.addIfCond((CaseWhenEval.IfThenEval) evalNodeMap.get(caseWhenProto.getIfConds(i))); } if (caseWhenProto.hasElse()) { caseWhenEval.setElseResult(evalNodeMap.get(caseWhenProto.getElse())); } current = caseWhenEval; } else if (type == EvalType.IF_THEN) { PlanProto.IfCondEval ifCondProto = protoNode.getIfCond(); current = new CaseWhenEval.IfThenEval(evalNodeMap.get(ifCondProto.getCondition()), evalNodeMap.get(ifCondProto.getThen())); } else if (EvalType.isFunction(type)) { PlanProto.FunctionEval funcProto = protoNode.getFunction(); EvalNode [] params = new EvalNode[funcProto.getParamIdsCount()]; for (int i = 0; i < funcProto.getParamIdsCount(); i++) { params[i] = evalNodeMap.get(funcProto.getParamIds(i)); } FunctionDesc funcDesc = null; try { funcDesc = new FunctionDesc(funcProto.getFuncion()); if (type == EvalType.FUNCTION) { current = new GeneralFunctionEval(context, funcDesc, params); if (evalContext != null && funcDesc.getInvocation().hasPython()) { evalContext.addScriptEngine(current, new PythonScriptEngine(funcDesc)); } } else if (type == EvalType.AGG_FUNCTION || type == EvalType.WINDOW_FUNCTION) { if (type == EvalType.AGG_FUNCTION) { AggregationFunctionCallEval aggFunc = new AggregationFunctionCallEval(new FunctionDesc(funcProto.getFuncion()), params); PlanProto.AggFunctionEvalSpec aggFunctionProto = protoNode.getAggFunction(); if (aggFunctionProto.getFirstPhase() && aggFunctionProto.getLastPhase()) { aggFunc.setFirstAndLastPhase(); } else if (aggFunctionProto.getFirstPhase()) { aggFunc.setFirstPhase(); } else if (aggFunctionProto.getLastPhase()) { aggFunc.setLastPhase(); } else { aggFunc.setIntermediatePhase(); } if (aggFunctionProto.hasAlias()) { aggFunc.setAlias(aggFunctionProto.getAlias()); } current = aggFunc; if (evalContext != null && funcDesc.getInvocation().hasPythonAggregation()) { evalContext.addScriptEngine(current, new PythonScriptEngine(funcDesc, aggFunc.isFirstPhase(), aggFunc.isLastPhase())); } } else { WinFunctionEvalSpec windowFuncProto = protoNode.getWinFunction(); WindowFunctionEval winFunc = new WindowFunctionEval(new FunctionDesc(funcProto.getFuncion()), params, convertWindowFrame(windowFuncProto.getWindowFrame())); if (windowFuncProto.getSortSpecCount() > 0) { SortSpec[] sortSpecs = LogicalNodeDeserializer.convertSortSpecs(windowFuncProto.getSortSpecList()); winFunc.setSortSpecs(sortSpecs); } current = winFunc; } } } catch (ClassNotFoundException cnfe) { String functionName = "Unknown"; DataType[] parameterTypes = new DataType[0]; if (funcProto.getFuncion() != null && funcProto.getFuncion().getSignature() != null) { FunctionSignatureProto funcSignatureProto = funcProto.getFuncion().getSignature(); if (funcSignatureProto.hasName()) { functionName = funcSignatureProto.getName(); } parameterTypes = funcSignatureProto.getParameterTypesList().toArray( new DataType[funcSignatureProto.getParameterTypesCount()]); } throw new TajoInternalError( new UndefinedFunctionException(buildSimpleFunctionSignature(functionName, parameterTypes)) ); } } else { throw new TajoInternalError("Unknown EvalType: " + type.name()); } evalNodeMap.put(protoNode.getId(), current); } return current; } private static WindowSpec.WindowFrame convertWindowFrame(WinFunctionEvalSpec.WindowFrame windowFrame) { WindowFrameStartBoundType startBoundType = convertWindowStartBound(windowFrame.getStartBound().getBoundType()); WindowSpec.WindowStartBound startBound = new WindowSpec.WindowStartBound(startBoundType); WindowFrameEndBoundType endBoundType = convertWindowEndBound(windowFrame.getEndBound().getBoundType()); WindowSpec.WindowEndBound endBound = new WindowSpec.WindowEndBound(endBoundType); WindowSpec.WindowFrame frame = new WindowSpec.WindowFrame(startBound, endBound); return frame; } private static WindowFrameStartBoundType convertWindowStartBound( WinFunctionEvalSpec.WindowFrameStartBoundType type) { if (type == WinFunctionEvalSpec.WindowFrameStartBoundType.S_UNBOUNDED_PRECEDING) { return WindowFrameStartBoundType.UNBOUNDED_PRECEDING; } else if (type == WinFunctionEvalSpec.WindowFrameStartBoundType.S_CURRENT_ROW) { return WindowFrameStartBoundType.CURRENT_ROW; } else if (type == WinFunctionEvalSpec.WindowFrameStartBoundType.S_PRECEDING) { return WindowFrameStartBoundType.PRECEDING; } else { throw new IllegalStateException("Unknown Window Start Bound type: " + type.name()); } } private static WindowFrameEndBoundType convertWindowEndBound( WinFunctionEvalSpec.WindowFrameEndBoundType type) { if (type == WinFunctionEvalSpec.WindowFrameEndBoundType.E_UNBOUNDED_FOLLOWING) { return WindowFrameEndBoundType.UNBOUNDED_FOLLOWING; } else if (type == WinFunctionEvalSpec.WindowFrameEndBoundType.E_CURRENT_ROW) { return WindowFrameEndBoundType.CURRENT_ROW; } else if (type == WinFunctionEvalSpec.WindowFrameEndBoundType.E_FOLLOWING) { return WindowFrameEndBoundType.FOLLOWING; } else { throw new IllegalStateException("Unknown Window Start Bound type: " + type.name()); } } public static Datum deserialize(PlanProto.Datum datum) { switch (datum.getType()) { case BOOLEAN: return DatumFactory.createBool(datum.getBoolean()); case CHAR: return DatumFactory.createChar(datum.getText()); case INT1: case INT2: return DatumFactory.createInt2((short) datum.getInt4()); case INT4: return DatumFactory.createInt4(datum.getInt4()); case INT8: return DatumFactory.createInt8(datum.getInt8()); case FLOAT4: return DatumFactory.createFloat4(datum.getFloat4()); case FLOAT8: return DatumFactory.createFloat8(datum.getFloat8()); case VARCHAR: case TEXT: return DatumFactory.createText(datum.getText()); case TIMESTAMP: return new TimestampDatum(datum.getInt8()); case DATE: return DatumFactory.createDate(datum.getInt4()); case TIME: return DatumFactory.createTime(datum.getInt8()); case BINARY: case BLOB: return DatumFactory.createBlob(datum.getBlob().toByteArray()); case INTERVAL: return new IntervalDatum(datum.getInterval().getMonth(), datum.getInterval().getMsec()); case NULL_TYPE: return NullDatum.get(); case ANY: return DatumFactory.createAny(deserialize(datum.getActual())); default: throw new RuntimeException("Unknown data type: " + datum.getType().name()); } } }