You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/04/18 11:20:02 UTC
[40/51] [partial] TAJO-752: Escalate sub modules in tajo-core into
the top-level modules. (hyunsik)
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
new file mode 100644
index 0000000..b8f3311
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
@@ -0,0 +1,739 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.algebra.*;
+
+import java.util.Stack;
+
+public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTEXT, RESULT> {
+
+ /**
+ * The prehook is called before each expression is visited.
+ */
+ public void preHook(CONTEXT ctx, Stack<Expr> stack, Expr expr) throws PlanningException {
+ }
+
+
+ /**
+ * The posthook is called before each expression is visited.
+ */
+ public RESULT postHook(CONTEXT ctx, Stack<Expr> stack, Expr expr, RESULT current) throws PlanningException {
+ return current;
+ }
+
+ /**
+ * visit visits each relational operator expression recursively.
+ *
+ * @param stack The stack contains the upper operators' type.
+ * @param expr The visiting relational operator
+ */
+ public RESULT visit(CONTEXT ctx, Stack<Expr> stack, Expr expr) throws PlanningException {
+ preHook(ctx, stack, expr);
+
+ RESULT current;
+
+ switch (expr.getType()) {
+
+ case Projection:
+ current = visitProjection(ctx, stack, (Projection) expr);
+ break;
+ case Limit:
+ current = visitLimit(ctx, stack, (Limit) expr);
+ break;
+ case Sort:
+ current = visitSort(ctx, stack, (Sort) expr);
+ break;
+ case Having:
+ current = visitHaving(ctx, stack, (Having) expr);
+ break;
+ case Aggregation:
+ current = visitGroupBy(ctx, stack, (Aggregation) expr);
+ break;
+ case Join:
+ current = visitJoin(ctx, stack, (Join) expr);
+ break;
+ case Filter:
+ current = visitFilter(ctx, stack, (Selection) expr);
+ break;
+ case Union:
+ current = visitUnion(ctx, stack, (SetOperation) expr);
+ break;
+ case Except:
+ current = visitExcept(ctx, stack, (SetOperation) expr);
+ break;
+ case Intersect:
+ current = visitIntersect(ctx, stack, (SetOperation) expr);
+ break;
+ case SimpleTableSubQuery:
+ current = visitSimpleTableSubQuery(ctx, stack, (SimpleTableSubQuery) expr);
+ break;
+ case TablePrimaryTableSubQuery:
+ current = visitTableSubQuery(ctx, stack, (TablePrimarySubQuery) expr);
+ break;
+ case RelationList:
+ current = visitRelationList(ctx, stack, (RelationList) expr);
+ break;
+ case Relation:
+ current = visitRelation(ctx, stack, (Relation) expr);
+ break;
+ case ScalarSubQuery:
+ current = visitScalarSubQuery(ctx, stack, (ScalarSubQuery) expr);
+ break;
+ case Explain:
+ current = visitExplain(ctx, stack, (Explain) expr);
+ break;
+
+ case CreateDatabase:
+ current = visitCreateDatabase(ctx, stack, (CreateDatabase) expr);
+ break;
+ case DropDatabase:
+ current = visitDropDatabase(ctx, stack, (DropDatabase) expr);
+ break;
+ case CreateTable:
+ current = visitCreateTable(ctx, stack, (CreateTable) expr);
+ break;
+ case DropTable:
+ current = visitDropTable(ctx, stack, (DropTable) expr);
+ break;
+ case AlterTablespace:
+ current = visitAlterTablespace(ctx, stack, (AlterTablespace) expr);
+ break;
+ case AlterTable:
+ current = visitAlterTable(ctx, stack, (AlterTable) expr);
+ break;
+
+ case Insert:
+ current = visitInsert(ctx, stack, (Insert) expr);
+ break;
+
+ case And:
+ current = visitAnd(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Or:
+ current = visitOr(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Not:
+ current = visitNot(ctx, stack, (NotExpr) expr);
+ break;
+
+ case Equals:
+ current = visitEquals(ctx, stack, (BinaryOperator) expr);
+ break;
+ case NotEquals:
+ current = visitNotEquals(ctx, stack, (BinaryOperator) expr);
+ break;
+ case LessThan:
+ current = visitLessThan(ctx, stack, (BinaryOperator) expr);
+ break;
+ case LessThanOrEquals:
+ current = visitLessThanOrEquals(ctx, stack, (BinaryOperator) expr);
+ break;
+ case GreaterThan:
+ current = visitGreaterThan(ctx, stack, (BinaryOperator) expr);
+ break;
+ case GreaterThanOrEquals:
+ current = visitGreaterThanOrEquals(ctx, stack, (BinaryOperator) expr);
+ break;
+
+ // Other Predicates
+ case Between:
+ current = visitBetween(ctx, stack, (BetweenPredicate) expr);
+ break;
+ case CaseWhen:
+ current = visitCaseWhen(ctx, stack, (CaseWhenPredicate) expr);
+ break;
+ case IsNullPredicate:
+ current = visitIsNullPredicate(ctx, stack, (IsNullPredicate) expr);
+ break;
+ case InPredicate:
+ current = visitInPredicate(ctx, stack, (InPredicate) expr);
+ break;
+ case ValueList:
+ current = visitValueListExpr(ctx, stack, (ValueListExpr) expr);
+ break;
+ case ExistsPredicate:
+ current = visitExistsPredicate(ctx, stack, (ExistsPredicate) expr);
+ break;
+
+ // String Operator or Pattern Matching Predicates
+ case LikePredicate:
+ current = visitLikePredicate(ctx, stack, (PatternMatchPredicate) expr);
+ break;
+ case SimilarToPredicate:
+ current = visitSimilarToPredicate(ctx, stack, (PatternMatchPredicate) expr);
+ break;
+ case Regexp:
+ current = visitRegexpPredicate(ctx, stack, (PatternMatchPredicate) expr);
+ break;
+ case Concatenate:
+ current = visitConcatenate(ctx, stack, (BinaryOperator) expr);
+ break;
+
+ // Arithmetic Operators
+ case Plus:
+ current = visitPlus(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Minus:
+ current = visitMinus(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Multiply:
+ current = visitMultiply(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Divide:
+ current = visitDivide(ctx, stack, (BinaryOperator) expr);
+ break;
+ case Modular:
+ current = visitModular(ctx, stack, (BinaryOperator) expr);
+ break;
+
+ // Other Expressions
+ case Sign:
+ current = visitSign(ctx, stack, (SignedExpr) expr);
+ break;
+ case Column:
+ current = visitColumnReference(ctx, stack, (ColumnReferenceExpr) expr);
+ break;
+ case Target:
+ current = visitTargetExpr(ctx, stack, (NamedExpr) expr);
+ break;
+ case Function:
+ current = visitFunction(ctx, stack, (FunctionExpr) expr);
+ break;
+ case Asterisk:
+ current = visitQualifiedAsterisk(ctx, stack, (QualifiedAsteriskExpr) expr);
+ break;
+
+
+ case CountRowsFunction:
+ current = visitCountRowsFunction(ctx, stack, (CountRowsFunctionExpr) expr);
+ break;
+ case GeneralSetFunction:
+ current = visitGeneralSetFunction(ctx, stack, (GeneralSetFunctionExpr) expr);
+ break;
+
+ case DataType:
+ current = visitDataType(ctx, stack, (DataTypeExpr) expr);
+ break;
+ case Cast:
+ current = visitCastExpr(ctx, stack, (CastExpr) expr);
+ break;
+ case Literal:
+ current = visitLiteral(ctx, stack, (LiteralValue) expr);
+ break;
+ case NullLiteral:
+ current = visitNullLiteral(ctx, stack, (NullLiteral) expr);
+ break;
+ case DateLiteral:
+ current = visitDateLiteral(ctx, stack, (DateLiteral) expr);
+ break;
+ case TimeLiteral:
+ current = visitTimeLiteral(ctx, stack, (TimeLiteral) expr);
+ break;
+ case TimestampLiteral:
+ current = visitTimestampLiteral(ctx, stack, (TimestampLiteral) expr);
+ break;
+
+
+
+ default:
+ throw new PlanningException("Cannot support this type algebra \"" + expr.getType() + "\"");
+ }
+
+ // skip postHook against only one relation
+ if (expr.getType() == OpType.RelationList) {
+ RelationList relationList = (RelationList)expr;
+ if (relationList.size() == 1 && relationList.getRelations()[0].getType() == OpType.Relation) {
+ return current;
+ }
+ }
+
+ postHook(ctx, stack, expr, current);
+ return current;
+ }
+
+ private RESULT visitDefaultUnaryExpr(CONTEXT ctx, Stack<Expr> stack, UnaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ RESULT child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return child;
+ }
+
+ private RESULT visitDefaultBinaryExpr(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr)
+ throws PlanningException {
+ stack.push(expr);
+ RESULT child = visit(ctx, stack, expr.getLeft());
+ visit(ctx, stack, expr.getRight());
+ stack.pop();
+ return child;
+ }
+
+ @Override
+ public RESULT visitProjection(CONTEXT ctx, Stack<Expr> stack, Projection expr) throws PlanningException {
+ stack.push(expr);
+ try {
+ for (NamedExpr target : expr.getNamedExprs()) {
+ visit(ctx, stack, target);
+ }
+ if (expr.hasChild()) {
+ return visit(ctx, stack, expr.getChild());
+ }
+ } finally {
+ stack.pop();
+ }
+ return null;
+ }
+
+ @Override
+ public RESULT visitLimit(CONTEXT ctx, Stack<Expr> stack, Limit expr) throws PlanningException {
+ stack.push(expr);
+ visit(ctx, stack, expr.getFetchFirstNum());
+ RESULT result = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitSort(CONTEXT ctx, Stack<Expr> stack, Sort expr) throws PlanningException {
+ stack.push(expr);
+ for (Sort.SortSpec sortSpec : expr.getSortSpecs()) {
+ visit(ctx, stack, sortSpec.getKey());
+ }
+ RESULT result = visit(ctx, stack, expr.getChild());
+ return result;
+ }
+
+ @Override
+ public RESULT visitHaving(CONTEXT ctx, Stack<Expr> stack, Having expr) throws PlanningException {
+ stack.push(expr);
+ visit(ctx, stack, expr.getQual());
+ RESULT result = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitGroupBy(CONTEXT ctx, Stack<Expr> stack, Aggregation expr) throws PlanningException {
+ stack.push(expr);
+
+ for (org.apache.tajo.algebra.Aggregation.GroupElement groupElement : expr.getGroupSet()) {
+ for (Expr groupingSet : groupElement.getGroupingSets()) {
+ visit(ctx, stack, groupingSet);
+ }
+ }
+
+ RESULT result = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitJoin(CONTEXT ctx, Stack<Expr> stack, Join expr) throws PlanningException {
+ stack.push(expr);
+ visit(ctx, stack, expr.getQual());
+ visit(ctx, stack, expr.getLeft());
+ RESULT result = visit(ctx, stack, expr.getRight());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitFilter(CONTEXT ctx, Stack<Expr> stack, Selection expr) throws PlanningException {
+ stack.push(expr);
+ visit(ctx, stack, expr.getQual());
+ RESULT result = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitUnion(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitExcept(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitIntersect(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitSimpleTableSubQuery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubQuery expr)
+ throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitTableSubQuery(CONTEXT ctx, Stack<Expr> stack, TablePrimarySubQuery expr)
+ throws PlanningException {
+ stack.push(expr);
+ RESULT child = visit(ctx, stack, expr.getSubQuery());
+ stack.pop();
+ return child;
+ }
+
+ @Override
+ public RESULT visitRelationList(CONTEXT ctx, Stack<Expr> stack, RelationList expr) throws PlanningException {
+ stack.push(expr);
+ RESULT child = null;
+ for (Expr e : expr.getRelations()) {
+ child = visit(ctx, stack, e);
+ }
+ stack.pop();
+ return child;
+ }
+
+ @Override
+ public RESULT visitRelation(CONTEXT ctx, Stack<Expr> stack, Relation expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitScalarSubQuery(CONTEXT ctx, Stack<Expr> stack, ScalarSubQuery expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitExplain(CONTEXT ctx, Stack<Expr> stack, Explain expr) throws PlanningException {
+ stack.push(expr);
+ RESULT child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return child;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Data Definition Language Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitCreateDatabase(CONTEXT ctx, Stack<Expr> stack, CreateDatabase expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitDropDatabase(CONTEXT ctx, Stack<Expr> stack, DropDatabase expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitCreateTable(CONTEXT ctx, Stack<Expr> stack, CreateTable expr) throws PlanningException {
+ stack.push(expr);
+ RESULT child = null;
+ if (expr.hasSubQuery()) {
+ child = visit(ctx, stack, expr.getSubQuery());
+ }
+ stack.pop();
+ return child;
+ }
+
+ @Override
+ public RESULT visitDropTable(CONTEXT ctx, Stack<Expr> stack, DropTable expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitAlterTablespace(CONTEXT ctx, Stack<Expr> stack, AlterTablespace expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitAlterTable(CONTEXT ctx, Stack<Expr> stack, AlterTable expr) throws PlanningException {
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Insert or Update Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public RESULT visitInsert(CONTEXT ctx, Stack<Expr> stack, Insert expr) throws PlanningException {
+ stack.push(expr);
+ RESULT child = visit(ctx, stack, expr.getSubQuery());
+ stack.pop();
+ return child;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Logical Operator Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitAnd(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitOr(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitNot(CONTEXT ctx, Stack<Expr> stack, NotExpr expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Comparison Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public RESULT visitEquals(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitNotEquals(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitLessThan(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitLessThanOrEquals(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitGreaterThan(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitGreaterThanOrEquals(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr)
+ throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Other Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitBetween(CONTEXT ctx, Stack<Expr> stack, BetweenPredicate expr) throws PlanningException {
+ stack.push(expr);
+ RESULT result = visit(ctx, stack, expr.predicand());
+ visit(ctx, stack, expr.begin());
+ visit(ctx, stack, expr.end());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitCaseWhen(CONTEXT ctx, Stack<Expr> stack, CaseWhenPredicate expr) throws PlanningException {
+ stack.push(expr);
+ RESULT result = null;
+ for (CaseWhenPredicate.WhenExpr when : expr.getWhens()) {
+ result = visit(ctx, stack, when.getCondition());
+ visit(ctx, stack, when.getResult());
+ }
+ if (expr.hasElseResult()) {
+ visit(ctx, stack, expr.getElseResult());
+ }
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitIsNullPredicate(CONTEXT ctx, Stack<Expr> stack, IsNullPredicate expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitInPredicate(CONTEXT ctx, Stack<Expr> stack, InPredicate expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitValueListExpr(CONTEXT ctx, Stack<Expr> stack, ValueListExpr expr) throws PlanningException {
+ stack.push(expr);
+ RESULT result = null;
+ for (Expr value : expr.getValues()) {
+ result = visit(ctx, stack, value);
+ }
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitExistsPredicate(CONTEXT ctx, Stack<Expr> stack, ExistsPredicate expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // String Operator or Pattern Matching Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public RESULT visitLikePredicate(CONTEXT ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitSimilarToPredicate(CONTEXT ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitRegexpPredicate(CONTEXT ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitConcatenate(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Arithmetic Operators
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitPlus(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitMinus(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitMultiply(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitDivide(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitModular(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitDefaultBinaryExpr(ctx, stack, expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Other Expressions
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitSign(CONTEXT ctx, Stack<Expr> stack, SignedExpr expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitColumnReference(CONTEXT ctx, Stack<Expr> stack, ColumnReferenceExpr expr)
+ throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitTargetExpr(CONTEXT ctx, Stack<Expr> stack, NamedExpr expr) throws PlanningException {
+ return visitDefaultUnaryExpr(ctx, stack, expr);
+ }
+
+ @Override
+ public RESULT visitFunction(CONTEXT ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
+ stack.push(expr);
+ RESULT result = null;
+ if (expr.hasParams()) {
+ for (Expr param : expr.getParams()) {
+ result = visit(ctx, stack, param);
+ }
+ }
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitQualifiedAsterisk(CONTEXT ctx, Stack<Expr> stack, QualifiedAsteriskExpr expr) throws PlanningException {
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // General Set Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitCountRowsFunction(CONTEXT ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
+ throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitGeneralSetFunction(CONTEXT ctx, Stack<Expr> stack, GeneralSetFunctionExpr expr)
+ throws PlanningException {
+ stack.push(expr);
+ RESULT result = null;
+ for (Expr param : expr.getParams()) {
+ result = visit(ctx, stack, param);
+ }
+ stack.pop();
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Literal Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public RESULT visitDataType(CONTEXT ctx, Stack<Expr> stack, DataTypeExpr expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitCastExpr(CONTEXT ctx, Stack<Expr> stack, CastExpr expr) throws PlanningException {
+ stack.push(expr);
+ RESULT result = visit(ctx, stack, expr.getOperand());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitLiteral(CONTEXT ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitNullLiteral(CONTEXT ctx, Stack<Expr> stack, NullLiteral expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitTimestampLiteral(CONTEXT ctx, Stack<Expr> stack, TimestampLiteral expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitDateLiteral(CONTEXT ctx, Stack<Expr> stack, DateLiteral expr) throws PlanningException {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
new file mode 100644
index 0000000..0f758bf
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
@@ -0,0 +1,319 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.engine.planner.logical.*;
+
+import java.util.Stack;
+
+public class BasicLogicalPlanVisitor<CONTEXT, RESULT> implements LogicalPlanVisitor<CONTEXT, RESULT> {
+
+ /**
+ * The prehook is called before each node is visited.
+ */
+ @SuppressWarnings("unused")
+ public void preHook(LogicalPlan plan, LogicalNode node, Stack<LogicalNode> stack, CONTEXT data)
+ throws PlanningException {
+ }
+
+ /**
+ * The posthook is called after each node is visited.
+ */
+ @SuppressWarnings("unused")
+ public void postHook(LogicalPlan plan, LogicalNode node, Stack<LogicalNode> stack, CONTEXT data)
+ throws PlanningException {
+ }
+
+ public CONTEXT visit(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block)
+ throws PlanningException {
+ visit(context, plan, block, block.getRoot(), new Stack<LogicalNode>());
+ return context;
+ }
+
+ /**
+ * visit visits each logicalNode recursively.
+ */
+ public RESULT visit(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, LogicalNode node,
+ Stack<LogicalNode> stack)
+ throws PlanningException {
+ RESULT current;
+ switch (node.getType()) {
+ case ROOT:
+ current = visitRoot(context, plan, block, (LogicalRootNode) node, stack);
+ break;
+ case EXPRS:
+ return null;
+ case PROJECTION:
+ current = visitProjection(context, plan, block, (ProjectionNode) node, stack);
+ break;
+ case LIMIT:
+ current = visitLimit(context, plan, block, (LimitNode) node, stack);
+ break;
+ case SORT:
+ current = visitSort(context, plan, block, (SortNode) node, stack);
+ break;
+ case HAVING:
+ current = visitHaving(context, plan, block, (HavingNode) node, stack);
+ break;
+ case GROUP_BY:
+ current = visitGroupBy(context, plan, block, (GroupbyNode) node, stack);
+ break;
+ case SELECTION:
+ current = visitFilter(context, plan, block, (SelectionNode) node, stack);
+ break;
+ case JOIN:
+ current = visitJoin(context, plan, block, (JoinNode) node, stack);
+ break;
+ case UNION:
+ current = visitUnion(context, plan, block, (UnionNode) node, stack);
+ break;
+ case EXCEPT:
+ current = visitExcept(context, plan, block, (ExceptNode) node, stack);
+ break;
+ case INTERSECT:
+ current = visitIntersect(context, plan, block, (IntersectNode) node, stack);
+ break;
+ case TABLE_SUBQUERY:
+ current = visitTableSubQuery(context, plan, block, (TableSubQueryNode) node, stack);
+ break;
+ case SCAN:
+ current = visitScan(context, plan, block, (ScanNode) node, stack);
+ break;
+ case PARTITIONS_SCAN:
+ current = visitPartitionedTableScan(context, plan, block, (PartitionedTableScanNode) node, stack);
+ break;
+ case STORE:
+ current = visitStoreTable(context, plan, block, (StoreTableNode) node, stack);
+ break;
+ case INSERT:
+ current = visitInsert(context, plan, block, (InsertNode) node, stack);
+ break;
+ case CREATE_DATABASE:
+ current = visitCreateDatabase(context, plan, block, (CreateDatabaseNode) node, stack);
+ break;
+ case DROP_DATABASE:
+ current = visitDropDatabase(context, plan, block, (DropDatabaseNode) node, stack);
+ break;
+ case CREATE_TABLE:
+ current = visitCreateTable(context, plan, block, (CreateTableNode) node, stack);
+ break;
+ case DROP_TABLE:
+ current = visitDropTable(context, plan, block, (DropTableNode) node, stack);
+ break;
+ case ALTER_TABLESPACE:
+ current = visitAlterTablespace(context, plan, block, (AlterTablespaceNode) node, stack);
+ break;
+ case ALTER_TABLE:
+ current = visitAlterTable(context, plan, block, (AlterTableNode) node, stack);
+ break;
+ default:
+ throw new PlanningException("Unknown logical node type: " + node.getType());
+ }
+
+ return current;
+ }
+
+ @Override
+ public RESULT visitRoot(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, LogicalRootNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitProjection(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, ProjectionNode node,
+ Stack<LogicalNode> stack)
+ throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitLimit(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, LimitNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitSort(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, SortNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitHaving(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, HavingNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitGroupBy(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitFilter(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, SelectionNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitJoin(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getLeftChild(), stack);
+ visit(context, plan, block, node.getRightChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitUnion(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, UnionNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ LogicalPlan.QueryBlock leftBlock = plan.getBlock(node.getLeftChild());
+ RESULT result = visit(context, plan, leftBlock, leftBlock.getRoot(), stack);
+ LogicalPlan.QueryBlock rightBlock = plan.getBlock(node.getRightChild());
+ visit(context, plan, rightBlock, rightBlock.getRoot(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitExcept(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, ExceptNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getLeftChild(), stack);
+ visit(context, plan, block, node.getRightChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitIntersect(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, IntersectNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getLeftChild(), stack);
+ visit(context, plan, block, node.getRightChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitTableSubQuery(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ LogicalPlan.QueryBlock childBlock = plan.getBlock(node.getSubQuery());
+ RESULT result = visit(context, plan, childBlock, childBlock.getRoot(), new Stack<LogicalNode>());
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitScan(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, ScanNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitPartitionedTableScan(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ PartitionedTableScanNode node, Stack<LogicalNode> stack)
+ throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitStoreTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, StoreTableNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitInsert(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, InsertNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ stack.push(node);
+ RESULT result = visit(context, plan, block, node.getChild(), stack);
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitCreateDatabase(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ CreateDatabaseNode node, Stack<LogicalNode> stack) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitDropDatabase(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropDatabaseNode node, Stack<LogicalNode> stack) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitCreateTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, CreateTableNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ RESULT result = null;
+ stack.push(node);
+ if (node.hasSubQuery()) {
+ result = visit(context, plan, block, node.getChild(), stack);
+ }
+ stack.pop();
+ return result;
+ }
+
+ @Override
+ public RESULT visitDropTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropTableNode node,
+ Stack<LogicalNode> stack) {
+ return null;
+ }
+
+ @Override
+ public RESULT visitAlterTablespace(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ AlterTablespaceNode node, Stack<LogicalNode> stack) throws PlanningException {
+ return null;
+ }
+
+ @Override
+ public RESULT visitAlterTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, AlterTableNode node,
+ Stack<LogicalNode> stack) {
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/BroadcastJoinPlanVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BroadcastJoinPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BroadcastJoinPlanVisitor.java
new file mode 100644
index 0000000..14ac85f
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BroadcastJoinPlanVisitor.java
@@ -0,0 +1,86 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.engine.planner.global.GlobalPlanner;
+import org.apache.tajo.engine.planner.logical.JoinNode;
+import org.apache.tajo.engine.planner.logical.LogicalNode;
+import org.apache.tajo.engine.planner.logical.NodeType;
+
+import java.util.Stack;
+
+public class BroadcastJoinPlanVisitor extends BasicLogicalPlanVisitor<GlobalPlanner.GlobalPlanContext, LogicalNode> {
+
+ @Override
+ public LogicalNode visitJoin(GlobalPlanner.GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ JoinNode node, Stack<LogicalNode> stack) throws PlanningException {
+ LogicalNode leftChild = node.getLeftChild();
+ LogicalNode rightChild = node.getRightChild();
+
+ if (isScanNode(leftChild) && isScanNode(rightChild)) {
+ node.setCandidateBroadcast(true);
+ node.getBroadcastTargets().add(leftChild);
+ node.getBroadcastTargets().add(rightChild);
+ return node;
+ }
+
+ if(!isScanNode(leftChild)) {
+ visit(context, plan, block, leftChild, stack);
+ }
+
+ if(!isScanNode(rightChild)) {
+ visit(context, plan, block, rightChild, stack);
+ }
+
+ if(isBroadcastCandidateNode(leftChild) && isBroadcastCandidateNode(rightChild)) {
+ node.setCandidateBroadcast(true);
+ if(leftChild.getType() == NodeType.JOIN) {
+ node.getBroadcastTargets().addAll(((JoinNode)leftChild).getBroadcastTargets());
+ } else {
+ node.getBroadcastTargets().add(leftChild);
+ }
+
+ if(rightChild.getType() == NodeType.JOIN) {
+ node.getBroadcastTargets().addAll(((JoinNode)rightChild).getBroadcastTargets());
+ } else {
+ node.getBroadcastTargets().add(rightChild);
+ }
+ }
+
+ return node;
+ }
+
+ private static boolean isBroadcastCandidateNode(LogicalNode node) {
+ if(node.getType() == NodeType.SCAN ||
+ node.getType() == NodeType.PARTITIONS_SCAN) {
+ return true;
+ }
+
+ if(node.getType() == NodeType.JOIN && ((JoinNode)node).isCandidateBroadcast()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean isScanNode(LogicalNode node) {
+ return node.getType() == NodeType.SCAN ||
+ node.getType() == NodeType.PARTITIONS_SCAN;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
new file mode 100644
index 0000000..9dd8700
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
@@ -0,0 +1,236 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.annotation.Nullable;
+import org.apache.tajo.engine.planner.logical.*;
+
+import java.util.Stack;
+
+/**
+ * It returns a list of node plan strings.
+ */
+public class ExplainLogicalPlanVisitor extends BasicLogicalPlanVisitor<ExplainLogicalPlanVisitor.Context, LogicalNode> {
+
+ public static class Context {
+ public int maxDepth = -1;
+ public int depth = 0;
+ public Stack<DepthString> explains = new Stack<DepthString>();
+
+ public void add(int depth, PlanString planString) {
+ maxDepth = Math.max(maxDepth, depth);
+ explains.push(new DepthString(depth, planString));
+ }
+
+ public int getMaxDepth() {
+ return this.maxDepth;
+ }
+
+ public Stack<DepthString> getExplains() {
+ return explains;
+ }
+ }
+
+ public static class DepthString {
+ private int depth;
+ private PlanString planStr;
+
+ DepthString(int depth, PlanString planStr) {
+ this.depth = depth;
+ this.planStr = planStr;
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ public PlanString getPlanString() {
+ return planStr;
+ }
+ }
+
+ public Context getBlockPlanStrings(@Nullable LogicalPlan plan, LogicalNode node) throws PlanningException {
+ Stack<LogicalNode> stack = new Stack<LogicalNode>();
+ Context explainContext = new Context();
+ visit(explainContext, plan, null, node, stack);
+ return explainContext;
+ }
+
+ @Override
+ public LogicalNode visitRoot(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, LogicalRootNode node, Stack<LogicalNode> stack)
+ throws PlanningException {
+ return visit(context, plan, block, node.getChild(), stack);
+ }
+
+ @Override
+ public LogicalNode visitProjection(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ ProjectionNode node, Stack<LogicalNode> stack)
+ throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitLimit(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ LimitNode node, Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitSort(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, SortNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ public LogicalNode visitHaving(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, HavingNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitGroupBy(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ private LogicalNode visitUnaryNode(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ UnaryNode node, Stack<LogicalNode> stack) throws PlanningException {
+ context.depth++;
+ stack.push(node);
+ visit(context, plan, block, node.getChild(), stack);
+ context.depth--;
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ private LogicalNode visitBinaryNode(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, BinaryNode node,
+ Stack<LogicalNode> stack)
+ throws PlanningException {
+ context.depth++;
+ stack.push(node);
+ visit(context, plan, block, node.getLeftChild(), stack);
+ visit(context, plan, block, node.getRightChild(), stack);
+ stack.pop();
+ context.depth--;
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitFilter(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, SelectionNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitJoin(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitBinaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitUnion(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, UnionNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitBinaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitExcept(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, ExceptNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitBinaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitIntersect(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, IntersectNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ return visitBinaryNode(context, plan, block, node, stack);
+ }
+
+ @Override
+ public LogicalNode visitTableSubQuery(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
+ context.depth++;
+ stack.push(node);
+ visit(context, plan, block, node.getSubQuery(), new Stack<LogicalNode>());
+ stack.pop();
+ context.depth--;
+ context.add(context.depth, node.getPlanString());
+
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitScan(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, ScanNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitPartitionedTableScan(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ PartitionedTableScanNode node, Stack<LogicalNode> stack)
+ throws PlanningException {
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitStoreTable(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ StoreTableNode node, Stack<LogicalNode> stack) throws PlanningException {
+ return visitUnaryNode(context, plan, block, node, stack);
+ }
+
+ public LogicalNode visitCreateDatabase(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ CreateDatabaseNode node, Stack<LogicalNode> stack) throws PlanningException {
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ public LogicalNode visitDropDatabase(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+ DropDatabaseNode node, Stack<LogicalNode> stack) throws PlanningException {
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ @Override
+ public LogicalNode visitInsert(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, InsertNode node,
+ Stack<LogicalNode> stack) throws PlanningException {
+ context.depth++;
+ stack.push(node);
+ super.visitInsert(context, plan, block, node, stack);
+ stack.pop();
+ context.depth--;
+ context.add(context.depth, node.getPlanString());
+ return node;
+ }
+
+ public static String printDepthString(int maxDepth, DepthString planStr) {
+ StringBuilder output = new StringBuilder();
+ String pad = new String(new char[planStr.getDepth() * 3]).replace('\0', ' ');
+ output.append(pad + planStr.getPlanString().getTitle()).append("\n");
+
+ for (String str : planStr.getPlanString().getExplanations()) {
+ output.append(pad).append(" => ").append(str).append("\n");
+ }
+
+ for (String str : planStr.getPlanString().getDetails()) {
+ output.append(pad).append(" => ").append(str).append("\n");
+ }
+ return output.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/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
new file mode 100644
index 0000000..1b57b98
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
@@ -0,0 +1,631 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.algebra.*;
+import org.apache.tajo.catalog.CatalogService;
+import org.apache.tajo.catalog.CatalogUtil;
+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.common.TajoDataTypes;
+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.planner.logical.NodeType;
+import org.apache.tajo.exception.InternalException;
+import org.joda.time.DateTime;
+
+import java.util.Stack;
+
+/**
+ * <code>ExprAnnotator</code> makes an annotated expression called <code>EvalNode</code> from an
+ * {@link org.apache.tajo.algebra.Expr}. It visits descendants recursively from a given expression, and finally
+ * it returns an EvalNode.
+ */
+public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, EvalNode> {
+ private CatalogService catalog;
+
+ public ExprAnnotator(CatalogService catalog) {
+ this.catalog = catalog;
+ }
+
+ static class Context {
+ LogicalPlan plan;
+ LogicalPlan.QueryBlock currentBlock;
+
+ public Context(LogicalPlan plan, LogicalPlan.QueryBlock block) {
+ this.plan = plan;
+ this.currentBlock = block;
+ }
+ }
+
+ public EvalNode createEvalNode(LogicalPlan plan, LogicalPlan.QueryBlock block, Expr expr)
+ throws PlanningException {
+ Context context = new Context(plan, block);
+ return visit(context, new Stack<Expr>(), expr);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Logical Operator Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitAnd(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.AND, left, right);
+ }
+
+ @Override
+ public EvalNode visitOr(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.OR, left, right);
+ }
+
+ @Override
+ public EvalNode visitNot(Context ctx, Stack<Expr> stack, NotExpr expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode child = visit(ctx, stack, expr.getChild());
+ stack.pop();
+ return new NotEval(child);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Comparison Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public EvalNode visitEquals(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitNotEquals(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitLessThan(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitLessThanOrEquals(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitGreaterThan(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitGreaterThanOrEquals(Context ctx, Stack<Expr> stack, BinaryOperator expr)
+ throws PlanningException {
+ return visitCommonComparison(ctx, stack, expr);
+ }
+
+ public EvalNode visitCommonComparison(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ EvalType evalType;
+ switch (expr.getType()) {
+ case Equals:
+ evalType = EvalType.EQUAL;
+ break;
+ case NotEquals:
+ evalType = EvalType.NOT_EQUAL;
+ break;
+ case LessThan:
+ evalType = EvalType.LTH;
+ break;
+ case LessThanOrEquals:
+ evalType = EvalType.LEQ;
+ break;
+ case GreaterThan:
+ evalType = EvalType.GTH;
+ break;
+ case GreaterThanOrEquals:
+ evalType = EvalType.GEQ;
+ break;
+ default:
+ throw new IllegalStateException("Wrong Expr Type: " + expr.getType());
+ }
+
+ return new BinaryEval(evalType, left, right);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Other Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitBetween(Context ctx, Stack<Expr> stack, BetweenPredicate between) throws PlanningException {
+ stack.push(between);
+ EvalNode predicand = visit(ctx, stack, between.predicand());
+ EvalNode begin = visit(ctx, stack, between.begin());
+ EvalNode end = visit(ctx, stack, between.end());
+ stack.pop();
+
+ BetweenPredicateEval betweenEval = new BetweenPredicateEval(
+ between.isNot(),
+ between.isSymmetric(),
+ predicand, begin, end);
+ return betweenEval;
+ }
+
+ @Override
+ public EvalNode visitCaseWhen(Context ctx, Stack<Expr> stack, CaseWhenPredicate caseWhen) throws PlanningException {
+ CaseWhenEval caseWhenEval = new CaseWhenEval();
+
+ EvalNode condition;
+ EvalNode result;
+ for (CaseWhenPredicate.WhenExpr when : caseWhen.getWhens()) {
+ condition = visit(ctx, stack, when.getCondition());
+ result = visit(ctx, stack, when.getResult());
+ caseWhenEval.addWhen(condition, result);
+ }
+
+ if (caseWhen.hasElseResult()) {
+ caseWhenEval.setElseResult(visit(ctx, stack, caseWhen.getElseResult()));
+ }
+
+ return caseWhenEval;
+ }
+
+ @Override
+ public EvalNode visitIsNullPredicate(Context ctx, Stack<Expr> stack, IsNullPredicate expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode child = visit(ctx, stack, expr.getPredicand());
+ stack.pop();
+ return new IsNullEval(expr.isNot(), child);
+ }
+
+ @Override
+ public EvalNode visitInPredicate(Context ctx, Stack<Expr> stack, InPredicate expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode lhs = visit(ctx, stack, expr.getLeft());
+ RowConstantEval rowConstantEval = (RowConstantEval) visit(ctx, stack, expr.getInValue());
+ stack.pop();
+ return new InEval(lhs, rowConstantEval, expr.isNot());
+ }
+
+ @Override
+ public EvalNode visitValueListExpr(Context ctx, Stack<Expr> stack, ValueListExpr expr) throws PlanningException {
+ Datum[] values = new Datum[expr.getValues().length];
+ EvalNode [] evalNodes = new EvalNode[expr.getValues().length];
+ for (int i = 0; i < expr.getValues().length; i++) {
+ evalNodes[i] = visit(ctx, stack, expr.getValues()[i]);
+ if (!EvalTreeUtil.checkIfCanBeConstant(evalNodes[i])) {
+ throw new PlanningException("Non constant values cannot be included in IN PREDICATE.");
+ }
+ values[i] = EvalTreeUtil.evaluateImmediately(evalNodes[i]);
+ }
+ return new RowConstantEval(values);
+ }
+
+ @Override
+ public EvalNode visitExistsPredicate(Context ctx, Stack<Expr> stack, ExistsPredicate expr) throws PlanningException {
+ throw new PlanningException("Cannot support EXISTS clause yet");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // String Operator or Pattern Matching Predicates Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public EvalNode visitLikePredicate(Context ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitPatternMatchPredicate(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitSimilarToPredicate(Context ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitPatternMatchPredicate(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitRegexpPredicate(Context ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ return visitPatternMatchPredicate(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitConcatenate(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.CONCATENATE, left, right);
+ }
+
+ private EvalNode visitPatternMatchPredicate(Context ctx, Stack<Expr> stack, PatternMatchPredicate expr)
+ throws PlanningException {
+ EvalNode field = visit(ctx, stack, expr.getPredicand());
+ ConstEval pattern = (ConstEval) visit(ctx, stack, expr.getPattern());
+
+ // A pattern is a const value in pattern matching predicates.
+ // In a binary expression, the result is always null if a const value in left or right side is null.
+ if (pattern.getValue() instanceof NullDatum) {
+ return new ConstEval(NullDatum.get());
+ } else {
+ if (expr.getType() == OpType.LikePredicate) {
+ return new LikePredicateEval(expr.isNot(), field, pattern, expr.isCaseInsensitive());
+ } else if (expr.getType() == OpType.SimilarToPredicate) {
+ return new SimilarToPredicateEval(expr.isNot(), field, pattern);
+ } else {
+ return new RegexPredicateEval(expr.isNot(), field, pattern, expr.isCaseInsensitive());
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Arithmetic Operators
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitPlus(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.PLUS, left, right);
+ }
+
+ @Override
+ public EvalNode visitMinus(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.MINUS, left, right);
+ }
+
+ @Override
+ public EvalNode visitMultiply(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.MULTIPLY, left, right);
+ }
+
+ @Override
+ public EvalNode visitDivide(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.DIVIDE, left, right);
+ }
+
+ @Override
+ public EvalNode visitModular(Context ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode left = visit(ctx, stack, expr.getLeft());
+ EvalNode right = visit(ctx, stack, expr.getRight());
+ stack.pop();
+
+ return new BinaryEval(EvalType.MODULAR, left, right);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Other Expressions
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitSign(Context ctx, Stack<Expr> stack, SignedExpr expr) throws PlanningException {
+ stack.push(expr);
+ EvalNode numericExpr = visit(ctx, stack, expr.getChild());
+ stack.pop();
+
+ if (expr.isNegative()) {
+ return new SignedEval(expr.isNegative(), numericExpr);
+ } else {
+ return numericExpr;
+ }
+ }
+
+ @Override
+ public EvalNode visitColumnReference(Context ctx, Stack<Expr> stack, ColumnReferenceExpr expr)
+ throws PlanningException {
+ Column column = ctx.plan.resolveColumn(ctx.currentBlock, expr);
+ return new FieldEval(column);
+ }
+
+ @Override
+ public EvalNode visitTargetExpr(Context ctx, Stack<Expr> stack, NamedExpr expr) throws PlanningException {
+ throw new PlanningException("ExprAnnotator cannot take NamedExpr");
+ }
+
+ @Override
+ public EvalNode visitFunction(Context ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
+ stack.push(expr); // <--- Push
+
+ // Given parameters
+ Expr[] params = expr.getParams();
+ if (params == null) {
+ params = new Expr[0];
+ }
+
+ EvalNode[] givenArgs = new EvalNode[params.length];
+ TajoDataTypes.DataType[] paramTypes = new TajoDataTypes.DataType[params.length];
+
+ for (int i = 0; i < params.length; i++) {
+ givenArgs[i] = visit(ctx, stack, params[i]);
+ paramTypes[i] = givenArgs[i].getValueType();
+ }
+
+ stack.pop(); // <--- Pop
+
+ if (!catalog.containFunction(expr.getSignature(), paramTypes)) {
+ throw new NoSuchFunctionException(expr.getSignature(), paramTypes);
+ }
+
+ FunctionDesc funcDesc = catalog.getFunction(expr.getSignature(), paramTypes);
+
+ try {
+ CatalogProtos.FunctionType functionType = funcDesc.getFuncType();
+ if (functionType == CatalogProtos.FunctionType.GENERAL
+ || functionType == CatalogProtos.FunctionType.UDF) {
+ return new GeneralFunctionEval(funcDesc, (GeneralFunction) funcDesc.newInstance(), givenArgs);
+ } else if (functionType == CatalogProtos.FunctionType.AGGREGATION
+ || functionType == CatalogProtos.FunctionType.UDA) {
+ if (!ctx.currentBlock.hasNode(NodeType.GROUP_BY)) {
+ ctx.currentBlock.setAggregationRequire();
+ }
+ return new AggregationFunctionCallEval(funcDesc, (AggFunction) funcDesc.newInstance(), givenArgs);
+ } else if (functionType == CatalogProtos.FunctionType.DISTINCT_AGGREGATION
+ || functionType == CatalogProtos.FunctionType.DISTINCT_UDA) {
+ throw new PlanningException("Unsupported function: " + funcDesc.toString());
+ } else {
+ throw new PlanningException("Unsupported Function Type: " + functionType.name());
+ }
+ } catch (InternalException e) {
+ throw new PlanningException(e);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // General Set Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitCountRowsFunction(Context ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
+ throws PlanningException {
+ FunctionDesc countRows = catalog.getFunction("count", CatalogProtos.FunctionType.AGGREGATION,
+ new TajoDataTypes.DataType[] {});
+ if (countRows == null) {
+ throw new NoSuchFunctionException(countRows.getSignature(), new TajoDataTypes.DataType[]{});
+ }
+
+ try {
+ ctx.currentBlock.setAggregationRequire();
+
+ return new AggregationFunctionCallEval(countRows, (AggFunction) countRows.newInstance(),
+ new EvalNode[] {});
+ } catch (InternalException e) {
+ throw new NoSuchFunctionException(countRows.getSignature(), new TajoDataTypes.DataType[]{});
+ }
+ }
+
+ @Override
+ public EvalNode visitGeneralSetFunction(Context ctx, Stack<Expr> stack, GeneralSetFunctionExpr setFunction)
+ throws PlanningException {
+
+ Expr[] params = setFunction.getParams();
+ EvalNode[] givenArgs = new EvalNode[params.length];
+ TajoDataTypes.DataType[] paramTypes = new TajoDataTypes.DataType[params.length];
+
+ CatalogProtos.FunctionType functionType = setFunction.isDistinct() ?
+ CatalogProtos.FunctionType.DISTINCT_AGGREGATION : CatalogProtos.FunctionType.AGGREGATION;
+ givenArgs[0] = visit(ctx, stack, params[0]);
+ if (setFunction.getSignature().equalsIgnoreCase("count")) {
+ paramTypes[0] = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.ANY);
+ } else {
+ paramTypes[0] = givenArgs[0].getValueType();
+ }
+
+ if (!catalog.containFunction(setFunction.getSignature(), functionType, paramTypes)) {
+ throw new NoSuchFunctionException(setFunction.getSignature(), paramTypes);
+ }
+
+ FunctionDesc funcDesc = catalog.getFunction(setFunction.getSignature(), functionType, paramTypes);
+ if (!ctx.currentBlock.hasNode(NodeType.GROUP_BY)) {
+ ctx.currentBlock.setAggregationRequire();
+ }
+
+ try {
+ return new AggregationFunctionCallEval(funcDesc, (AggFunction) funcDesc.newInstance(), givenArgs);
+ } catch (InternalException e) {
+ throw new PlanningException(e);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Literal Section
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public EvalNode visitDataType(Context ctx, Stack<Expr> stack, DataTypeExpr expr) throws PlanningException {
+ return super.visitDataType(ctx, stack, expr);
+ }
+
+ @Override
+ public EvalNode visitCastExpr(Context ctx, Stack<Expr> stack, CastExpr expr) throws PlanningException {
+ EvalNode child = super.visitCastExpr(ctx, stack, expr);
+
+ if (child.getType() == EvalType.CONST) { // if it is a casting operation for a constant value
+ ConstEval constEval = (ConstEval) child; // it will be pre-computed and casted to a constant value
+ return new ConstEval(DatumFactory.cast(constEval.getValue(), LogicalPlanner.convertDataType(expr.getTarget())));
+ } else {
+ return new CastEval(child, LogicalPlanner.convertDataType(expr.getTarget()));
+ }
+ }
+
+ @Override
+ public EvalNode visitLiteral(Context ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException {
+ switch (expr.getValueType()) {
+ case Boolean:
+ return new ConstEval(DatumFactory.createBool(((BooleanLiteral) expr).isTrue()));
+ case String:
+ return new ConstEval(DatumFactory.createText(expr.getValue()));
+ case Unsigned_Integer:
+ return new ConstEval(DatumFactory.createInt4(expr.getValue()));
+ case Unsigned_Large_Integer:
+ return new ConstEval(DatumFactory.createInt8(expr.getValue()));
+ case Unsigned_Float:
+ return new ConstEval(DatumFactory.createFloat8(expr.getValue()));
+ default:
+ throw new RuntimeException("Unsupported type: " + expr.getValueType());
+ }
+ }
+
+ @Override
+ public EvalNode visitNullLiteral(Context ctx, Stack<Expr> stack, NullLiteral expr) throws PlanningException {
+ return new ConstEval(NullDatum.get());
+ }
+
+ @Override
+ public EvalNode visitDateLiteral(Context context, Stack<Expr> stack, DateLiteral expr) throws PlanningException {
+ DateValue dateValue = expr.getDate();
+ int [] dates = dateToIntArray(dateValue.getYears(), dateValue.getMonths(), dateValue.getDays());
+ return new ConstEval(new DateDatum(dates[0], dates[1], dates[2]));
+ }
+
+ @Override
+ public EvalNode visitTimestampLiteral(Context ctx, Stack<Expr> stack, TimestampLiteral expr)
+ throws PlanningException {
+ DateValue dateValue = expr.getDate();
+ TimeValue timeValue = expr.getTime();
+
+ int [] dates = dateToIntArray(dateValue.getYears(),
+ dateValue.getMonths(),
+ dateValue.getDays());
+ int [] times = timeToIntArray(timeValue.getHours(),
+ timeValue.getMinutes(),
+ timeValue.getSeconds(),
+ timeValue.getSecondsFraction());
+ DateTime dateTime;
+ if (timeValue.hasSecondsFraction()) {
+ dateTime = new DateTime(dates[0], dates[1], dates[2], times[0], times[1], times[2], times[3]);
+ } else {
+ dateTime = new DateTime(dates[0], dates[1], dates[2], times[0], times[1], times[2]);
+ }
+
+ return new ConstEval(new TimestampDatum(dateTime));
+ }
+
+ @Override
+ public EvalNode visitTimeLiteral(Context ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
+ TimeValue timeValue = expr.getTime();
+ int [] times = timeToIntArray(timeValue.getHours(),
+ timeValue.getMinutes(),
+ timeValue.getSeconds(),
+ timeValue.getSecondsFraction());
+
+ TimeDatum datum;
+ if (timeValue.hasSecondsFraction()) {
+ datum = new TimeDatum(times[0], times[1], times[2], times[3]);
+ } else {
+ datum = new TimeDatum(times[0], times[1], times[2]);
+ }
+ return new ConstEval(datum);
+ }
+
+ public static int [] dateToIntArray(String years, String months, String days)
+ throws PlanningException {
+ int year = Integer.valueOf(years);
+ int month = Integer.valueOf(months);
+ int day = Integer.valueOf(days);
+
+ if (!(1 <= year && year <= 9999)) {
+ throw new PlanningException(String.format("Years (%d) must be between 1 and 9999 integer value", year));
+ }
+
+ if (!(1 <= month && month <= 12)) {
+ throw new PlanningException(String.format("Months (%d) must be between 1 and 12 integer value", month));
+ }
+
+ if (!(1<= day && day <= 31)) {
+ throw new PlanningException(String.format("Days (%d) must be between 1 and 31 integer value", day));
+ }
+
+ int [] results = new int[3];
+ results[0] = year;
+ results[1] = month;
+ results[2] = day;
+
+ return results;
+ }
+
+ public static int [] timeToIntArray(String hours, String minutes, String seconds, String fractionOfSecond)
+ throws PlanningException {
+ int hour = Integer.valueOf(hours);
+ int minute = Integer.valueOf(minutes);
+ int second = Integer.valueOf(seconds);
+ int fraction = 0;
+ if (fractionOfSecond != null) {
+ fraction = Integer.valueOf(fractionOfSecond);
+ }
+
+ if (!(0 <= hour && hour <= 23)) {
+ throw new PlanningException(String.format("Hours (%d) must be between 0 and 24 integer value", hour));
+ }
+
+ if (!(0 <= minute && minute <= 59)) {
+ throw new PlanningException(String.format("Minutes (%d) must be between 0 and 59 integer value", minute));
+ }
+
+ if (!(0 <= second && second <= 59)) {
+ throw new PlanningException(String.format("Seconds (%d) must be between 0 and 59 integer value", second));
+ }
+
+ if (fraction != 0) {
+ if (!(0 <= fraction && fraction <= 999)) {
+ throw new PlanningException(String.format("Seconds (%d) must be between 0 and 999 integer value", fraction));
+ }
+ }
+
+ int [] results = new int[4];
+ results[0] = hour;
+ results[1] = minute;
+ results[2] = second;
+ results[3] = fraction;
+
+ return results;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprFinder.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprFinder.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprFinder.java
new file mode 100644
index 0000000..89eed91
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprFinder.java
@@ -0,0 +1,74 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.algebra.BinaryOperator;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.algebra.OpType;
+import org.apache.tajo.algebra.UnaryOperator;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+
+class ExprFinder extends SimpleAlgebraVisitor<ExprFinder.Context, Object> {
+
+ static class Context {
+ Set<Expr> set = new HashSet<Expr>();
+ OpType targetType;
+
+ Context(OpType type) {
+ this.targetType = type;
+ }
+ }
+
+ public static <T extends Expr> Set<T> finds(Expr expr, OpType type) {
+ Context context = new Context(type);
+ ExprFinder finder = new ExprFinder();
+ Stack<Expr> stack = new Stack<Expr>();
+ stack.push(expr);
+ try {
+ finder.visit(context, new Stack<Expr>(), expr);
+ } catch (PlanningException e) {
+ throw new RuntimeException(e);
+ }
+ stack.pop();
+ return (Set<T>) context.set;
+ }
+
+ public Object visit(Context ctx, Stack<Expr> stack, Expr expr) throws PlanningException {
+ if (expr instanceof UnaryOperator) {
+ preHook(ctx, stack, expr);
+ visitUnaryOperator(ctx, stack, (UnaryOperator) expr);
+ postHook(ctx, stack, expr, null);
+ } else if (expr instanceof BinaryOperator) {
+ preHook(ctx, stack, expr);
+ visitBinaryOperator(ctx, stack, (BinaryOperator) expr);
+ postHook(ctx, stack, expr, null);
+ } else {
+ super.visit(ctx, stack, expr);
+ }
+
+ if (ctx.targetType == expr.getType()) {
+ ctx.set.add(expr);
+ }
+
+ return null;
+ }
+}