TAJO-1008: Protocol buffer De/Serialization for EvalNode.
Project: http://git-wip-us.apache.org/repos/asf/tajo/repo Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/ea5ce54d Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/ea5ce54d Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/ea5ce54d Branch: refs/heads/master Commit: ea5ce54d812ac38dc23683c48d0ca24fb122a7ba Parents: 2d6bff7 Author: Hyunsik Choi <[email protected]> Authored: Wed Aug 20 16:46:00 2014 +0900 Committer: Hyunsik Choi <[email protected]> Committed: Wed Aug 20 16:47:15 2014 +0900 ---------------------------------------------------------------------- CHANGES | 2 + .../main/java/org/apache/tajo/datum/Datum.java | 3 +- tajo-core/pom.xml | 1 + .../apache/tajo/engine/eval/AlgebraicUtil.java | 6 +- .../tajo/engine/eval/BetweenPredicateEval.java | 26 ++ .../org/apache/tajo/engine/eval/BinaryEval.java | 6 + .../apache/tajo/engine/eval/CaseWhenEval.java | 38 ++- .../org/apache/tajo/engine/eval/ConstEval.java | 12 +- .../org/apache/tajo/engine/eval/EvalNode.java | 4 + .../apache/tajo/engine/eval/EvalTreeUtil.java | 2 +- .../org/apache/tajo/engine/eval/EvalType.java | 46 ++- .../org/apache/tajo/engine/eval/FieldEval.java | 12 +- .../apache/tajo/engine/eval/FunctionEval.java | 14 + .../tajo/engine/eval/RowConstantEval.java | 10 + .../tajo/engine/eval/SimpleEvalNodeVisitor.java | 24 +- .../org/apache/tajo/engine/eval/UnaryEval.java | 11 + .../engine/plan/EvalTreeProtoDeserializer.java | 218 +++++++++++++ .../engine/plan/EvalTreeProtoSerializer.java | 308 +++++++++++++++++++ .../tajo/engine/planner/ExprAnnotator.java | 2 +- tajo-core/src/main/proto/Plan.proto | 209 +++++++++++++ .../apache/tajo/engine/eval/ExprTestBase.java | 21 ++ .../apache/tajo/engine/eval/TestEvalTree.java | 21 +- 22 files changed, 967 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index a4160aa..07119bd 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,8 @@ Release 0.9.0 - unreleased IMPROVEMENT + TAJO-1008: Protocol buffer De/Serialization for EvalNode. (hyunsik) + TAJO-984: Improve the default data type handling in RowStoreUtil. (jihoon via hyunsik) http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-common/src/main/java/org/apache/tajo/datum/Datum.java ---------------------------------------------------------------------- diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Datum.java index 874004b..f21e3d7 100644 --- a/tajo-common/src/main/java/org/apache/tajo/datum/Datum.java +++ b/tajo-common/src/main/java/org/apache/tajo/datum/Datum.java @@ -69,9 +69,8 @@ public abstract class Datum implements Comparable<Datum>, GsonObject { throw new InvalidCastException(type, Type.INT2); } public int asInt4() { - throw new InvalidCastException(type, Type.INT1); + throw new InvalidCastException(type, Type.INT4); } - public long asInt8() { throw new InvalidCastException(type, Type.INT8); } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/pom.xml ---------------------------------------------------------------------- diff --git a/tajo-core/pom.xml b/tajo-core/pom.xml index 32ae266..364c428 100644 --- a/tajo-core/pom.xml +++ b/tajo-core/pom.xml @@ -144,6 +144,7 @@ <argument>src/main/proto/TajoMasterProtocol.proto</argument> <argument>src/main/proto/TajoWorkerProtocol.proto</argument> <argument>src/main/proto/InternalTypes.proto</argument> + <argument>src/main/proto/Plan.proto</argument> </arguments> </configuration> <goals> http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java index a72e2a8..518b72f 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java @@ -72,7 +72,7 @@ public class AlgebraicUtil { EvalNode lTerm = null; EvalNode rTerm = null; - if (EvalType.isArithmeticOperator(left)) { // we can ensure that left is binary. + if (EvalType.isArithmeticOperator(left.getType())) { // we can ensure that left is binary. // If the left-left term is a variable, the left-right term is transposed. if (EvalTreeUtil.containColumnRef(((BinaryEval)left).getLeftExpr(), target)) { @@ -234,7 +234,7 @@ public class AlgebraicUtil { */ public static PartialBinaryExpr splitLeftTerm(BinaryEval binary) { - if (!(EvalType.isArithmeticOperator(binary))) { + if (!(EvalType.isArithmeticOperator(binary.getType()))) { throw new AlgebraicException("Invalid algebraic operation: " + binary); } @@ -259,7 +259,7 @@ public class AlgebraicUtil { */ public static PartialBinaryExpr splitRightTerm(BinaryEval binary) { - if (!(EvalType.isArithmeticOperator(binary))) { + if (!(EvalType.isArithmeticOperator(binary.getType()))) { throw new AlgebraicException("Invalid algebraic operation: " + binary); } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java index 0b9c7c1..07f9a6f 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BetweenPredicateEval.java @@ -46,6 +46,14 @@ public class BetweenPredicateEval extends EvalNode implements Cloneable { this.end = end; } + public boolean isNot() { + return not; + } + + public boolean isSymmetric() { + return symmetric; + } + public void setPredicand(EvalNode predicand) { this.predicand = predicand; } @@ -170,6 +178,24 @@ public class BetweenPredicateEval extends EvalNode implements Cloneable { } @Override + public int childNum() { + return 3; + } + + @Override + public EvalNode getChild(int idx) { + if (idx == 0) { + return predicand; + } else if (idx == 1) { + return begin; + } else if (idx == 2) { + return end; + } else { + throw new ArrayIndexOutOfBoundsException(idx); + } + } + + @Override public String getName() { return "between"; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java index 2430020..4c94f4a 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java @@ -88,6 +88,12 @@ public class BinaryEval extends EvalNode implements Cloneable { return (T) this.rightExpr; } + @Override + public int childNum() { + return 2; + } + + @Override public EvalNode getChild(int id) { if (id == 0) { return this.leftExpr; http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/CaseWhenEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/CaseWhenEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/CaseWhenEval.java index cf1acdf..3eb3a69 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/CaseWhenEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/CaseWhenEval.java @@ -43,7 +43,11 @@ public class CaseWhenEval extends EvalNode implements GsonObject { super(EvalType.CASE); } - public void addWhen(EvalNode condition, EvalNode result) { + public void addIfCond(IfThenEval ifCond) { + whens.add(ifCond); + } + + public void addIfCond(EvalNode condition, EvalNode result) { whens.add(new IfThenEval(condition, result)); } @@ -80,6 +84,22 @@ public class CaseWhenEval extends EvalNode implements GsonObject { } @Override + public int childNum() { + return whens.size() + (elseResult != null ? 1 : 0); + } + + @Override + public EvalNode getChild(int idx) { + if (idx < whens.size()) { + return whens.get(idx); + } else if (idx == whens.size()) { + return elseResult; + } else { + throw new ArrayIndexOutOfBoundsException(idx); + } + } + + @Override public String getName() { return "?"; } @@ -175,6 +195,22 @@ public class CaseWhenEval extends EvalNode implements GsonObject { } @Override + public int childNum() { + return 2; + } + + @Override + public EvalNode getChild(int idx) { + if (idx == 0) { + return condition; + } else if (idx == 1) { + return result; + } else { + throw new ArrayIndexOutOfBoundsException(idx); + } + } + + @Override public String getName() { return "when?"; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/ConstEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/ConstEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/ConstEval.java index 2cb530d..323a4e9 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/ConstEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/ConstEval.java @@ -52,7 +52,17 @@ public class ConstEval extends EvalNode implements Comparable<ConstEval>, Clonea return CatalogUtil.newSimpleDataType(datum.type()); } - @Override + @Override + public int childNum() { + return 0; + } + + @Override + public EvalNode getChild(int idx) { + return null; + } + + @Override public String getName() { return this.datum.toString(); } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java index a30d27f..48ab516 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNode.java @@ -46,6 +46,10 @@ public abstract class EvalNode implements Cloneable, GsonObject { } public abstract DataType getValueType(); + + public abstract int childNum(); + + public abstract EvalNode getChild(int idx); public abstract String getName(); http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java index 3e7a05b..0035636 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalTreeUtil.java @@ -283,7 +283,7 @@ public class EvalTreeUtil { if (expr instanceof BinaryEval) { boolean joinComparator; if (includeThetaJoin) { - joinComparator = EvalType.isComparisonOperator(expr); + joinComparator = EvalType.isComparisonOperator(expr.getType()); } else { joinComparator = expr.getType() == EvalType.EQUAL; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java index 549f8d0..500928a 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java @@ -79,8 +79,34 @@ public enum EvalType { this.operatorName = text; } - public static boolean isLogicalOperator(EvalNode evalNode) { - EvalType type = evalNode.getType(); + public static boolean isUnaryOperator(EvalType type) { + boolean match = false; + + match |= type == CAST; + match |= type == IS_NULL; + match |= type == NOT; + match |= type == SIGNED; + + return match; + } + + public static boolean isBinaryOperator(EvalType type) { + boolean match = false; + + match |= isArithmeticOperator(type); + match |= isLogicalOperator(type) && type != NOT; + match |= isComparisonOperator(type) && type != BETWEEN; + + match |= type == CONCATENATE; + match |= type == IN; + match |= type == LIKE; + match |= type == REGEX; + match |= type == SIMILAR_TO; + + return match; + } + + public static boolean isLogicalOperator(EvalType type) { boolean match = false; match |= type == AND; @@ -90,8 +116,7 @@ public enum EvalType { return match; } - public static boolean isComparisonOperator(EvalNode evalNode) { - EvalType type = evalNode.getType(); + public static boolean isComparisonOperator(EvalType type) { boolean match = false; match |= type == EQUAL; @@ -105,8 +130,7 @@ public enum EvalType { return match; } - public static boolean isArithmeticOperator(EvalNode evalNode) { - EvalType type = evalNode.getType(); + public static boolean isArithmeticOperator(EvalType type) { boolean match = false; match |= type == PLUS; @@ -118,6 +142,16 @@ public enum EvalType { return match; } + public static boolean isFunction(EvalType type) { + boolean match = false; + + match |= type == FUNCTION; + match |= type == AGG_FUNCTION; + match |= type == WINDOW_FUNCTION; + + return match; + } + public String getOperatorName() { return operatorName != null ? operatorName : name(); } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/FieldEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FieldEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FieldEval.java index 20af854..5cedbca 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FieldEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FieldEval.java @@ -59,7 +59,17 @@ public class FieldEval extends EvalNode implements Cloneable { public DataType getValueType() { return column.getDataType(); } - + + @Override + public int childNum() { + return 0; + } + + @Override + public EvalNode getChild(int idx) { + return null; + } + public Column getColumnRef() { return column; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java index 376ee2e..9447ef2 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java @@ -79,6 +79,20 @@ public abstract class FunctionEval extends EvalNode implements Cloneable { this.argEvals = args; } + @Override + public int childNum() { + if (argEvals != null) { + return argEvals.length; + } else { + return 0; + } + } + + @Override + public EvalNode getChild(int idx) { + return argEvals[idx]; + } + public DataType getValueType() { return this.funcDesc.getReturnType(); } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/RowConstantEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/RowConstantEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/RowConstantEval.java index ffaf6b5..5b21be0 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/RowConstantEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/RowConstantEval.java @@ -42,6 +42,16 @@ public class RowConstantEval extends EvalNode { } @Override + public int childNum() { + return 0; + } + + @Override + public EvalNode getChild(int idx) { + return null; + } + + @Override public String getName() { return "ROW"; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java index 93f1f74..15e34de 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java @@ -26,7 +26,7 @@ import java.util.Stack; * It provides simple visitor methods for an expression tree. Since <code>SimpleEvalNodeVisitor</code> provides * fewer visitor methods, it allows users to write a simple rewriter for expression trees. */ -public class SimpleEvalNodeVisitor<CONTEXT> { +public abstract class SimpleEvalNodeVisitor<CONTEXT> { public EvalNode visit(CONTEXT context, EvalNode evalNode, Stack<EvalNode> stack) { EvalNode result; @@ -83,14 +83,14 @@ public class SimpleEvalNodeVisitor<CONTEXT> { return result; } - public EvalNode visitUnaryEval(CONTEXT context, Stack<EvalNode> stack, UnaryEval unaryEval) { + protected EvalNode visitUnaryEval(CONTEXT context, Stack<EvalNode> stack, UnaryEval unaryEval) { stack.push(unaryEval); visit(context, unaryEval.getChild(), stack); stack.pop(); return unaryEval; } - public EvalNode visitBinaryEval(CONTEXT context, Stack<EvalNode> stack, BinaryEval binaryEval) { + protected EvalNode visitBinaryEval(CONTEXT context, Stack<EvalNode> stack, BinaryEval binaryEval) { stack.push(binaryEval); visit(context, binaryEval.getLeftExpr(), stack); visit(context, binaryEval.getRightExpr(), stack); @@ -98,7 +98,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { return binaryEval; } - private EvalNode visitDefaultFunctionEval(CONTEXT context, Stack<EvalNode> stack, FunctionEval functionEval) { + protected EvalNode visitDefaultFunctionEval(CONTEXT context, Stack<EvalNode> stack, FunctionEval functionEval) { stack.push(functionEval); if (functionEval.getArgs() != null) { for (EvalNode arg : functionEval.getArgs()) { @@ -113,15 +113,15 @@ public class SimpleEvalNodeVisitor<CONTEXT> { // Value and Literal /////////////////////////////////////////////////////////////////////////////////////////////// - public EvalNode visitConst(CONTEXT context, ConstEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitConst(CONTEXT context, ConstEval evalNode, Stack<EvalNode> stack) { return evalNode; } - public EvalNode visitRowConstant(CONTEXT context, RowConstantEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitRowConstant(CONTEXT context, RowConstantEval evalNode, Stack<EvalNode> stack) { return evalNode; } - public EvalNode visitField(CONTEXT context, Stack<EvalNode> stack, FieldEval evalNode) { + protected EvalNode visitField(CONTEXT context, Stack<EvalNode> stack, FieldEval evalNode) { return evalNode; } @@ -130,7 +130,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { // SQL standard predicates /////////////////////////////////////////////////////////////////////////////////////////////// - public EvalNode visitBetween(CONTEXT context, BetweenPredicateEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitBetween(CONTEXT context, BetweenPredicateEval evalNode, Stack<EvalNode> stack) { stack.push(evalNode); visit(context, evalNode.getPredicand(), stack); visit(context, evalNode.getBegin(), stack); @@ -138,7 +138,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { return evalNode; } - public EvalNode visitCaseWhen(CONTEXT context, CaseWhenEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitCaseWhen(CONTEXT context, CaseWhenEval evalNode, Stack<EvalNode> stack) { stack.push(evalNode); for (CaseWhenEval.IfThenEval ifThenEval : evalNode.getIfThenEvals()) { visitIfThen(context, ifThenEval, stack); @@ -150,7 +150,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { return evalNode; } - public EvalNode visitIfThen(CONTEXT context, CaseWhenEval.IfThenEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitIfThen(CONTEXT context, CaseWhenEval.IfThenEval evalNode, Stack<EvalNode> stack) { stack.push(evalNode); visit(context, evalNode.getCondition(), stack); visit(context, evalNode.getResult(), stack); @@ -158,7 +158,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { return evalNode; } - public EvalNode visitInPredicate(CONTEXT context, InEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitInPredicate(CONTEXT context, InEval evalNode, Stack<EvalNode> stack) { return visitBinaryEval(context, stack, evalNode); } @@ -166,7 +166,7 @@ public class SimpleEvalNodeVisitor<CONTEXT> { // Functions /////////////////////////////////////////////////////////////////////////////////////////////// - public EvalNode visitFuncCall(CONTEXT context, FunctionEval evalNode, Stack<EvalNode> stack) { + protected EvalNode visitFuncCall(CONTEXT context, FunctionEval evalNode, Stack<EvalNode> stack) { return visitDefaultFunctionEval(context, stack, evalNode); } } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/eval/UnaryEval.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/UnaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/UnaryEval.java index 7c6833f..e7ae112 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/UnaryEval.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/UnaryEval.java @@ -19,6 +19,7 @@ package org.apache.tajo.engine.eval; import com.google.common.base.Objects; +import com.google.common.base.Preconditions; import com.google.gson.annotations.Expose; import org.apache.tajo.catalog.Schema; import org.apache.tajo.common.TajoDataTypes; @@ -37,6 +38,16 @@ public abstract class UnaryEval extends EvalNode implements Cloneable { this.child = child; } + @Override + public int childNum() { + return 1; + } + + public EvalNode getChild(int idx) { + Preconditions.checkArgument(idx == 0, "UnaryEval always has one child."); + return child; + } + public void setChild(EvalNode child) { this.child = child; } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoDeserializer.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoDeserializer.java b/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoDeserializer.java new file mode 100644 index 0000000..0fa6dbb --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoDeserializer.java @@ -0,0 +1,218 @@ +/* + * 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.engine.plan; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.tajo.catalog.Column; +import org.apache.tajo.catalog.FunctionDesc; +import org.apache.tajo.catalog.exception.NoSuchFunctionException; +import org.apache.tajo.catalog.proto.CatalogProtos; +import org.apache.tajo.datum.*; +import org.apache.tajo.engine.eval.*; +import org.apache.tajo.engine.function.AggFunction; +import org.apache.tajo.engine.function.GeneralFunction; +import org.apache.tajo.engine.plan.proto.PlanProto; +import org.apache.tajo.exception.InternalException; + +import java.util.*; + +/** + * It deserializes a serialized eval tree consisting of a number of EvalNodes. + * + * {@link org.apache.tajo.engine.plan.EvalTreeProtoSerializer} 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 org.apache.tajo.engine.plan.EvalTreeProtoSerializer + */ +public class EvalTreeProtoDeserializer { + + public static EvalNode deserialize(PlanProto.EvalTree 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. + + Iterator<PlanProto.EvalNode> it = nodeList.iterator(); + while (it.hasNext()) { + PlanProto.EvalNode protoNode = it.next(); + + 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(child, 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, (RowConstantEval) rhs, binProto.getNegative()); + 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.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) { + GeneralFunction instance = (GeneralFunction) funcDesc.newInstance(); + current = new GeneralFunctionEval(new FunctionDesc(funcProto.getFuncion()), instance, params); + } else if (type == EvalType.AGG_FUNCTION || type == EvalType.WINDOW_FUNCTION) { + AggFunction instance = (AggFunction) funcDesc.newInstance(); + if (type == EvalType.AGG_FUNCTION) { + current = new AggregationFunctionCallEval(new FunctionDesc(funcProto.getFuncion()), instance, params); + } else { + current = new WindowFunctionEval(new FunctionDesc(funcProto.getFuncion()), instance, params, null); + } + } + } catch (ClassNotFoundException cnfe) { + throw new NoSuchFunctionException(funcDesc.getSignature(), funcDesc.getParamTypes()); + } catch (InternalException ie) { + throw new NoSuchFunctionException(funcDesc.getSignature(), funcDesc.getParamTypes()); + } + } else { + throw new RuntimeException("Unknown EvalType: " + type.name()); + } + + evalNodeMap.put(protoNode.getId(), current); + } + + return current; + } + + 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(); + default: + throw new RuntimeException("Unknown data type: " + datum.getType().name()); + } + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoSerializer.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoSerializer.java b/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoSerializer.java new file mode 100644 index 0000000..d2b6fd7 --- /dev/null +++ b/tajo-core/src/main/java/org/apache/tajo/engine/plan/EvalTreeProtoSerializer.java @@ -0,0 +1,308 @@ +/* + * 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.engine.plan; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.protobuf.ByteString; +import org.apache.tajo.datum.Datum; +import org.apache.tajo.datum.IntervalDatum; +import org.apache.tajo.engine.eval.*; +import org.apache.tajo.engine.plan.proto.PlanProto; + +import java.util.Map; +import java.util.Stack; + +/** + * It traverses an eval tree consisting of a number of {@link org.apache.tajo.engine.eval.EvalNode} + * in a postfix traverse order. The postfix traverse order guarantees that all child nodes of some node N + * were already visited when the node N is visited. This manner makes tree serialization possible in a simple logic. + */ +public class EvalTreeProtoSerializer + extends SimpleEvalNodeVisitor<EvalTreeProtoSerializer.EvalTreeProtoBuilderContext> { + + private static final EvalTreeProtoSerializer instance; + + static { + instance = new EvalTreeProtoSerializer(); + } + + public static class EvalTreeProtoBuilderContext { + private int seqId = 0; + private Map<EvalNode, Integer> idMap = Maps.newHashMap(); + private PlanProto.EvalTree.Builder treeBuilder = PlanProto.EvalTree.newBuilder(); + } + + public static PlanProto.EvalTree serialize(EvalNode evalNode) { + EvalTreeProtoSerializer.EvalTreeProtoBuilderContext context = + new EvalTreeProtoSerializer.EvalTreeProtoBuilderContext(); + instance.visit(context, evalNode, new Stack<EvalNode>()); + return context.treeBuilder.build(); + } + + /** + * Return child's serialization IDs. Usually, 0 is used for a child id of unary node or left child of + * binary node. 1 is used for right child of binary node. Between will use 0 as predicand, 1 as begin, and 2 as + * end eval node. For more detail, you should refer to each EvalNode implementation. + * + * @param context Context + * @param evalNode EvalNode + * @return The array of IDs which points to stored EvalNode. + * @see org.apache.tajo.engine.eval.EvalNode + */ + private int [] registerGetChildIds(EvalTreeProtoBuilderContext context, EvalNode evalNode) { + int [] childIds = new int[evalNode.childNum()]; + for (int i = 0; i < evalNode.childNum(); i++) { + if (context.idMap.containsKey(evalNode.getChild(i))) { + childIds[i] = context.idMap.get(evalNode.getChild(i)); + } else { + childIds[i] = context.seqId++; + } + } + return childIds; + } + + private PlanProto.EvalNode.Builder createEvalBuilder(EvalTreeProtoBuilderContext context, EvalNode node) { + int sid; // serialization sequence id + if (context.idMap.containsKey(node)) { + sid = context.idMap.get(node); + } else { + sid = context.seqId++; + context.idMap.put(node, sid); + } + + PlanProto.EvalNode.Builder nodeBuilder = PlanProto.EvalNode.newBuilder(); + nodeBuilder.setId(sid); + nodeBuilder.setDataType(node.getValueType()); + nodeBuilder.setType(PlanProto.EvalType.valueOf(node.getType().name())); + return nodeBuilder; + } + + @Override + public EvalNode visitUnaryEval(EvalTreeProtoBuilderContext context, Stack<EvalNode> stack, UnaryEval unary) { + // visiting and registering childs + super.visitUnaryEval(context, stack, unary); + int [] childIds = registerGetChildIds(context, unary); + + // building itself + PlanProto.UnaryEval.Builder unaryBuilder = PlanProto.UnaryEval.newBuilder(); + unaryBuilder.setChildId(childIds[0]); + if (unary.getType() == EvalType.IS_NULL) { + IsNullEval isNullEval = (IsNullEval) unary; + unaryBuilder.setNegative(isNullEval.isNot()); + } else if (unary.getType() == EvalType.SIGNED) { + SignedEval signedEval = (SignedEval) unary; + unaryBuilder.setNegative(signedEval.isNegative()); + } else if (unary.getType() == EvalType.CAST) { + CastEval castEval = (CastEval) unary; + unaryBuilder.setCastingType(castEval.getValueType()); + } + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, unary); + builder.setUnary(unaryBuilder); + context.treeBuilder.addNodes(builder); + return unary; + } + + @Override + public EvalNode visitBinaryEval(EvalTreeProtoBuilderContext context, Stack<EvalNode> stack, BinaryEval binary) { + // visiting and registering childs + super.visitBinaryEval(context, stack, binary); + int [] childIds = registerGetChildIds(context, binary); + + // building itself + PlanProto.BinaryEval.Builder binaryBuilder = PlanProto.BinaryEval.newBuilder(); + binaryBuilder.setLhsId(childIds[0]); + binaryBuilder.setRhsId(childIds[1]); + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, binary); + builder.setBinary(binaryBuilder); + context.treeBuilder.addNodes(builder); + return binary; + } + + @Override + public EvalNode visitConst(EvalTreeProtoBuilderContext context, ConstEval constant, Stack<EvalNode> stack) { + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, constant); + builder.setConst(PlanProto.ConstEval.newBuilder().setValue(serialize(constant.getValue()))); + context.treeBuilder.addNodes(builder); + return constant; + } + + @Override + public EvalNode visitRowConstant(EvalTreeProtoBuilderContext context, RowConstantEval rowConst, + Stack<EvalNode> stack) { + + PlanProto.RowConstEval.Builder rowConstBuilder = PlanProto.RowConstEval.newBuilder(); + for (Datum d : rowConst.getValues()) { + rowConstBuilder.addValues(serialize(d)); + } + + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, rowConst); + builder.setRowConst(rowConstBuilder); + context.treeBuilder.addNodes(builder); + return rowConst; + } + + public EvalNode visitField(EvalTreeProtoBuilderContext context, Stack<EvalNode> stack, FieldEval field) { + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, field); + builder.setField(field.getColumnRef().getProto()); + context.treeBuilder.addNodes(builder); + return field; + } + + public EvalNode visitBetween(EvalTreeProtoBuilderContext context, BetweenPredicateEval between, + Stack<EvalNode> stack) { + // visiting and registering childs + super.visitBetween(context, between, stack); + int [] childIds = registerGetChildIds(context, between); + Preconditions.checkState(childIds.length == 3, "Between must have three childs, but there are " + childIds.length + + " child nodes"); + + // building itself + PlanProto.BetweenEval.Builder betweenBuilder = PlanProto.BetweenEval.newBuilder(); + betweenBuilder.setNegative(between.isNot()); + betweenBuilder.setSymmetric(between.isSymmetric()); + betweenBuilder.setPredicand(childIds[0]); + betweenBuilder.setBegin(childIds[1]); + betweenBuilder.setEnd(childIds[2]); + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, between); + builder.setBetween(betweenBuilder); + context.treeBuilder.addNodes(builder); + return between; + } + + public EvalNode visitCaseWhen(EvalTreeProtoBuilderContext context, CaseWhenEval caseWhen, Stack<EvalNode> stack) { + // visiting and registering childs + super.visitCaseWhen(context, caseWhen, stack); + int [] childIds = registerGetChildIds(context, caseWhen); + Preconditions.checkState(childIds.length > 0, "Case When must have at least one child, but there is no child"); + + // building itself + PlanProto.CaseWhenEval.Builder caseWhenBuilder = PlanProto.CaseWhenEval.newBuilder(); + int ifCondsNum = childIds.length - (caseWhen.hasElse() ? 1 : 0); + for (int i = 0; i < ifCondsNum; i++) { + caseWhenBuilder.addIfConds(childIds[i]); + } + if (caseWhen.hasElse()) { + caseWhenBuilder.setElse(childIds[childIds.length - 1]); + } + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, caseWhen); + builder.setCasewhen(caseWhenBuilder); + context.treeBuilder.addNodes(builder); + + return caseWhen; + } + + public EvalNode visitIfThen(EvalTreeProtoBuilderContext context, CaseWhenEval.IfThenEval ifCond, + Stack<EvalNode> stack) { + // visiting and registering childs + super.visitIfThen(context, ifCond, stack); + int [] childIds = registerGetChildIds(context, ifCond); + + // building itself + PlanProto.IfCondEval.Builder ifCondBuilder = PlanProto.IfCondEval.newBuilder(); + ifCondBuilder.setCondition(childIds[0]); + ifCondBuilder.setThen(childIds[1]); + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, ifCond); + builder.setIfCond(ifCondBuilder); + context.treeBuilder.addNodes(builder); + + return ifCond; + } + + public EvalNode visitFuncCall(EvalTreeProtoBuilderContext context, FunctionEval function, Stack<EvalNode> stack) { + // visiting and registering childs + super.visitFuncCall(context, function, stack); + int [] childIds = registerGetChildIds(context, function); + + // building itself + PlanProto.FunctionEval.Builder funcBuilder = PlanProto.FunctionEval.newBuilder(); + funcBuilder.setFuncion(function.getFuncDesc().getProto()); + for (int i = 0; i < childIds.length; i++) { + funcBuilder.addParamIds(childIds[i]); + } + + // registering itself and building EvalNode + PlanProto.EvalNode.Builder builder = createEvalBuilder(context, function); + builder.setFunction(funcBuilder); + context.treeBuilder.addNodes(builder); + + return function; + } + + public static PlanProto.Datum serialize(Datum datum) { + PlanProto.Datum.Builder builder = PlanProto.Datum.newBuilder(); + + builder.setType(datum.type()); + + switch (datum.type()) { + case NULL_TYPE: + break; + case BOOLEAN: + builder.setBoolean(datum.asBool()); + break; + case INT1: + case INT2: + case INT4: + case DATE: + builder.setInt4(datum.asInt4()); + break; + case INT8: + case TIMESTAMP: + case TIME: + builder.setInt8(datum.asInt8()); + break; + case FLOAT4: + builder.setFloat4(datum.asFloat4()); + break; + case FLOAT8: + builder.setFloat8(datum.asFloat8()); + break; + case CHAR: + case VARCHAR: + case TEXT: + builder.setText(datum.asChars()); + break; + case BINARY: + case BLOB: + builder.setBlob(ByteString.copyFrom(datum.asByteArray())); + break; + case INTERVAL: + IntervalDatum interval = (IntervalDatum) datum; + PlanProto.Interval.Builder intervalBuilder = PlanProto.Interval.newBuilder(); + intervalBuilder.setMonth(interval.getMonths()); + intervalBuilder.setMsec(interval.getMilliSeconds()); + builder.setInterval(intervalBuilder); + break; + default: + throw new RuntimeException("Unknown data type: " + datum.type().name()); + } + + return builder.build(); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java index 87e54de..6a3af98 100644 --- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java +++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java @@ -352,7 +352,7 @@ public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, Eva for (CaseWhenPredicate.WhenExpr when : caseWhen.getWhens()) { condition = visit(ctx, stack, when.getCondition()); result = visit(ctx, stack, when.getResult()); - caseWhenEval.addWhen(condition, result); + caseWhenEval.addIfCond(condition, result); } if (caseWhen.hasElseResult()) { http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/main/proto/Plan.proto ---------------------------------------------------------------------- diff --git a/tajo-core/src/main/proto/Plan.proto b/tajo-core/src/main/proto/Plan.proto new file mode 100644 index 0000000..fa8cbb8 --- /dev/null +++ b/tajo-core/src/main/proto/Plan.proto @@ -0,0 +1,209 @@ +/** + * 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 tajo.plan; +option cc_generic_services = false; +option java_package = "org.apache.tajo.engine.plan.proto"; +option java_outer_classname = "PlanProto"; +option java_generic_services = false; +option java_generate_equals_and_hash = true; + +import "PrimitiveProtos.proto"; +import "CatalogProtos.proto"; +import "DataTypes.proto"; + +enum NodeType { + BST_INDEX_SCAN = 0; + EXCEPT = 1; + EXPRS = 2; + DISTINCT_GROUP_BY = 3; + GROUP_BY = 4; + HAVING = 5; + JOIN = 6; + INSERT = 7; + INTERSECT = 8; + LIMIT = 9; + PARTITIONS_SCAN = 10; + PROJECTION = 11; + ROOT = 12; + SCAN = 13; + SELECTION = 14; + SORT = 15; + STORE = 16; + TABLE_SUBQUERY = 17; + UNION = 18; + WINDOW_AGG = 19; + + CREATE_DATABASE = 20; + DROP_DATABASE = 21; + CREATE_TABLE = 22; + DROP_TABLE = 23; + ALTER_TABLESPACE = 24; + ALTER_TABLE = 25; + TRUNCATE_TABLE = 26; +} + +message LogicalPlan { + required KeyValueSetProto adjacentList = 1; +} + +message LogicalNode { + required int32 pid = 1; + required NodeType type = 2; + required SchemaProto in_schema = 3; + required SchemaProto out_schema = 4; + required NodeSpec spec = 5; +} + +message NodeSpec { + optional ScanNode scan = 1; +} + +message ScanNode { + required TableDescProto table = 1; + optional string alias = 2; + required SchemaProto schema = 3; +} + + +enum EvalType { + NOT = 0; + AND = 1; + OR = 2; + EQUAL = 3; + IS_NULL = 4; + NOT_EQUAL = 5; + LTH = 6; + LEQ = 7; + GTH = 8; + GEQ = 9; + PLUS = 10; + MINUS = 11; + MODULAR = 12; + MULTIPLY = 13; + DIVIDE = 14; + + // Binary Bitwise expressions + BIT_AND = 15; + BIT_OR = 16; + BIT_XOR = 17; + + // Function + WINDOW_FUNCTION = 18; + AGG_FUNCTION = 19; + FUNCTION = 20; + + // String operator or pattern matching predicates + LIKE = 21; + SIMILAR_TO = 22; + REGEX = 23; + CONCATENATE = 24; + + // Other predicates + BETWEEN = 25; + CASE = 26; + IF_THEN = 27; + IN = 28; + + // Value or Reference + SIGNED = 29; + CAST = 30; + ROW_CONSTANT = 31; + FIELD = 32; + CONST = 33; +} + +message EvalTree { + repeated EvalNode nodes = 1; +} + +message EvalNode { + required int32 id = 1; + required EvalType type = 2; + required DataType data_type = 3; + + optional UnaryEval unary = 4; // NOT + optional BinaryEval binary = 5; + optional ConstEval const = 6; + optional ColumnProto field = 7; // field eval + optional FunctionEval function = 8; + optional RowConstEval rowConst = 9; + optional BetweenEval between = 10; + optional CaseWhenEval casewhen = 11; + optional IfCondEval ifCond = 12; +} + +message UnaryEval { + required int32 child_id = 1; + optional DataType castingType = 2; + optional bool negative = 3; +} + +message BinaryEval { + required int32 lhs_id = 1; + required int32 rhs_id = 2; + optional bool negative = 3 [default = false]; +} + +message BetweenEval { + required int32 predicand = 1; + required int32 begin = 2; + required int32 end = 3; + optional bool negative = 4 [default = false]; + optional bool symmetric = 5 [default = false]; +} + +message CaseWhenEval { + repeated int32 ifConds = 1; + optional int32 else = 2; +} + +message IfCondEval { + required int32 condition = 1; + required int32 then = 2; +} + +message ConstEval { + required Datum value = 1; +} + +message RowConstEval { + repeated Datum values = 1; +} + +message FunctionEval { + required FunctionDescProto funcion = 1; + repeated int32 paramIds = 2; +} + +message Datum { + required Type type = 1; + optional bool boolean = 2; + optional int32 int4 = 3; // int4, date + optional int64 int8 = 4; // int8, timestamp, and time + optional float float4 = 5; + optional double float8 = 6; + optional string text = 7; + optional bytes blob = 8; + optional Interval interval = 12; +} + +message Interval { + optional int32 month = 1 [default = 0]; + optional int64 msec = 2; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java b/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java index 7f9436a..3bee4f3 100644 --- a/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java +++ b/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java @@ -31,6 +31,9 @@ import org.apache.tajo.conf.TajoConf; import org.apache.tajo.datum.*; import org.apache.tajo.engine.json.CoreGsonHelper; import org.apache.tajo.engine.parser.SQLAnalyzer; +import org.apache.tajo.engine.plan.EvalTreeProtoDeserializer; +import org.apache.tajo.engine.plan.EvalTreeProtoSerializer; +import org.apache.tajo.engine.plan.proto.PlanProto; import org.apache.tajo.engine.planner.*; import org.apache.tajo.engine.query.QueryContext; import org.apache.tajo.engine.utils.SchemaUtil; @@ -53,6 +56,7 @@ import static org.apache.tajo.TajoConstants.DEFAULT_DATABASE_NAME; import static org.apache.tajo.TajoConstants.DEFAULT_TABLESPACE_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; public class ExprTestBase { private static TajoTestingCluster util; @@ -135,9 +139,21 @@ public class ExprTestBase { if (targets == null) { throw new PlanningException("Wrong query statement or query plan: " + parsedResults.get(0).getHistoryStatement()); } + + // Trying regression test for cloning, (de)serialization for json and protocol buffer + for (Target t : targets) { + try { + assertEquals(t.getEvalTree(), t.getEvalTree().clone()); + } catch (CloneNotSupportedException e) { + fail(e.getMessage()); + } + } for (Target t : targets) { assertJsonSerDer(t.getEvalTree()); } + for (Target t : targets) { + assertEvalTreeProtoSerDer(t.getEvalTree()); + } return targets; } @@ -226,4 +242,9 @@ public class ExprTestBase { } } } + + public static void assertEvalTreeProtoSerDer(EvalNode evalNode) { + PlanProto.EvalTree converted = EvalTreeProtoSerializer.serialize(evalNode); + assertEquals(evalNode, EvalTreeProtoDeserializer.deserialize(converted)); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/ea5ce54d/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestEvalTree.java ---------------------------------------------------------------------- diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestEvalTree.java b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestEvalTree.java index da43025..beeb1c6 100644 --- a/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestEvalTree.java +++ b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestEvalTree.java @@ -31,7 +31,7 @@ import org.junit.Test; import static org.apache.tajo.common.TajoDataTypes.Type.*; import static org.junit.Assert.*; -public class TestEvalTree extends ExprTestBase{ +public class TestEvalTree extends ExprTestBase { @Test public void testTupleEval() throws CloneNotSupportedException { ConstEval e1 = new ConstEval(DatumFactory.createInt4(1)); @@ -89,6 +89,15 @@ public class TestEvalTree extends ExprTestBase{ return CatalogUtil.newSimpleDataType(BOOLEAN); } + @Override + public int childNum() { + return 0; + } + + @Override + public EvalNode getChild(int idx) { + return null; + } } public static class MockFalseExpr extends EvalNode { @@ -126,6 +135,16 @@ public class TestEvalTree extends ExprTestBase{ public DataType getValueType() { return CatalogUtil.newSimpleDataType(BOOLEAN); } + + @Override + public int childNum() { + return 0; + } + + @Override + public EvalNode getChild(int idx) { + return null; + } } @Test
