Return-Path: X-Original-To: apmail-tajo-commits-archive@minotaur.apache.org Delivered-To: apmail-tajo-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 023041089D for ; Fri, 18 Apr 2014 09:20:02 +0000 (UTC) Received: (qmail 79715 invoked by uid 500); 18 Apr 2014 09:20:01 -0000 Delivered-To: apmail-tajo-commits-archive@tajo.apache.org Received: (qmail 79665 invoked by uid 500); 18 Apr 2014 09:20:00 -0000 Mailing-List: contact commits-help@tajo.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tajo.apache.org Delivered-To: mailing list commits@tajo.apache.org Received: (qmail 76562 invoked by uid 99); 18 Apr 2014 09:19:28 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 18 Apr 2014 09:19:28 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id DC138981231; Fri, 18 Apr 2014 09:19:24 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: hyunsik@apache.org To: commits@tajo.apache.org Date: Fri, 18 Apr 2014 09:20:02 -0000 Message-Id: In-Reply-To: <9171a3b9a1e24dccae6e386c4cc8efec@git.apache.org> References: <9171a3b9a1e24dccae6e386c4cc8efec@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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 implements AlgebraVisitor { + + /** + * The prehook is called before each expression is visited. + */ + public void preHook(CONTEXT ctx, Stack stack, Expr expr) throws PlanningException { + } + + + /** + * The posthook is called before each expression is visited. + */ + public RESULT postHook(CONTEXT ctx, Stack 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 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 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 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 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 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 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 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 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 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 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 stack, SetOperation expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitExcept(CONTEXT ctx, Stack stack, SetOperation expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitIntersect(CONTEXT ctx, Stack stack, SetOperation expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitSimpleTableSubQuery(CONTEXT ctx, Stack stack, SimpleTableSubQuery expr) + throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitTableSubQuery(CONTEXT ctx, Stack 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 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 stack, Relation expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitScalarSubQuery(CONTEXT ctx, Stack stack, ScalarSubQuery expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitExplain(CONTEXT ctx, Stack 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 stack, CreateDatabase expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitDropDatabase(CONTEXT ctx, Stack stack, DropDatabase expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitCreateTable(CONTEXT ctx, Stack 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 stack, DropTable expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitAlterTablespace(CONTEXT ctx, Stack stack, AlterTablespace expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitAlterTable(CONTEXT ctx, Stack stack, AlterTable expr) throws PlanningException { + return null; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Insert or Update Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public RESULT visitInsert(CONTEXT ctx, Stack 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 stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitOr(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitNot(CONTEXT ctx, Stack stack, NotExpr expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Comparison Predicates Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Override + public RESULT visitEquals(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitNotEquals(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitLessThan(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitLessThanOrEquals(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitGreaterThan(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitGreaterThanOrEquals(CONTEXT ctx, Stack stack, BinaryOperator expr) + throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Other Predicates Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public RESULT visitBetween(CONTEXT ctx, Stack 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 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 stack, IsNullPredicate expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitInPredicate(CONTEXT ctx, Stack stack, InPredicate expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitValueListExpr(CONTEXT ctx, Stack 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 stack, ExistsPredicate expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // String Operator or Pattern Matching Predicates Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Override + public RESULT visitLikePredicate(CONTEXT ctx, Stack stack, PatternMatchPredicate expr) + throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitSimilarToPredicate(CONTEXT ctx, Stack stack, PatternMatchPredicate expr) + throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitRegexpPredicate(CONTEXT ctx, Stack stack, PatternMatchPredicate expr) + throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitConcatenate(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Arithmetic Operators + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public RESULT visitPlus(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitMinus(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitMultiply(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitDivide(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitModular(CONTEXT ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitDefaultBinaryExpr(ctx, stack, expr); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Other Expressions + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public RESULT visitSign(CONTEXT ctx, Stack stack, SignedExpr expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitColumnReference(CONTEXT ctx, Stack stack, ColumnReferenceExpr expr) + throws PlanningException { + return null; + } + + @Override + public RESULT visitTargetExpr(CONTEXT ctx, Stack stack, NamedExpr expr) throws PlanningException { + return visitDefaultUnaryExpr(ctx, stack, expr); + } + + @Override + public RESULT visitFunction(CONTEXT ctx, Stack 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 stack, QualifiedAsteriskExpr expr) throws PlanningException { + return null; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // General Set Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public RESULT visitCountRowsFunction(CONTEXT ctx, Stack stack, CountRowsFunctionExpr expr) + throws PlanningException { + return null; + } + + @Override + public RESULT visitGeneralSetFunction(CONTEXT ctx, Stack 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 stack, DataTypeExpr expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitCastExpr(CONTEXT ctx, Stack 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 stack, LiteralValue expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitNullLiteral(CONTEXT ctx, Stack stack, NullLiteral expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitTimestampLiteral(CONTEXT ctx, Stack stack, TimestampLiteral expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitTimeLiteral(CONTEXT ctx, Stack stack, TimeLiteral expr) throws PlanningException { + return null; + } + + @Override + public RESULT visitDateLiteral(CONTEXT ctx, Stack 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 implements LogicalPlanVisitor { + + /** + * The prehook is called before each node is visited. + */ + @SuppressWarnings("unused") + public void preHook(LogicalPlan plan, LogicalNode node, Stack stack, CONTEXT data) + throws PlanningException { + } + + /** + * The posthook is called after each node is visited. + */ + @SuppressWarnings("unused") + public void postHook(LogicalPlan plan, LogicalNode node, Stack 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()); + return context; + } + + /** + * visit visits each logicalNode recursively. + */ + public RESULT visit(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, LogicalNode node, + Stack 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 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 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 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 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 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 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 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 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 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 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 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 stack) throws PlanningException { + stack.push(node); + LogicalPlan.QueryBlock childBlock = plan.getBlock(node.getSubQuery()); + RESULT result = visit(context, plan, childBlock, childBlock.getRoot(), new Stack()); + stack.pop(); + return result; + } + + @Override + public RESULT visitScan(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, ScanNode node, + Stack stack) throws PlanningException { + return null; + } + + @Override + public RESULT visitPartitionedTableScan(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, + PartitionedTableScanNode node, Stack stack) + throws PlanningException { + return null; + } + + @Override + public RESULT visitStoreTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, StoreTableNode node, + Stack 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 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 stack) throws PlanningException { + return null; + } + + @Override + public RESULT visitDropDatabase(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, DropDatabaseNode node, Stack stack) throws PlanningException { + return null; + } + + @Override + public RESULT visitCreateTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, CreateTableNode node, + Stack 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 stack) { + return null; + } + + @Override + public RESULT visitAlterTablespace(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, + AlterTablespaceNode node, Stack stack) throws PlanningException { + return null; + } + + @Override + public RESULT visitAlterTable(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, AlterTableNode node, + Stack 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 { + + @Override + public LogicalNode visitJoin(GlobalPlanner.GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, + JoinNode node, Stack 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 { + + public static class Context { + public int maxDepth = -1; + public int depth = 0; + public Stack explains = new Stack(); + + 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 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 stack = new Stack(); + 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 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 stack) + throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitLimit(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, + LimitNode node, Stack stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitSort(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, SortNode node, + Stack stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + public LogicalNode visitHaving(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, HavingNode node, + Stack stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitGroupBy(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node, + Stack stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + private LogicalNode visitUnaryNode(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, + UnaryNode node, Stack 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 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 stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitJoin(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode node, + Stack stack) throws PlanningException { + return visitBinaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitUnion(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, UnionNode node, + Stack stack) throws PlanningException { + return visitBinaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitExcept(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, ExceptNode node, + Stack stack) throws PlanningException { + return visitBinaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitIntersect(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, IntersectNode node, + Stack stack) throws PlanningException { + return visitBinaryNode(context, plan, block, node, stack); + } + + @Override + public LogicalNode visitTableSubQuery(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, + TableSubQueryNode node, Stack stack) throws PlanningException { + context.depth++; + stack.push(node); + visit(context, plan, block, node.getSubQuery(), new Stack()); + 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 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 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 stack) throws PlanningException { + return visitUnaryNode(context, plan, block, node, stack); + } + + public LogicalNode visitCreateDatabase(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, + CreateDatabaseNode node, Stack stack) throws PlanningException { + context.add(context.depth, node.getPlanString()); + return node; + } + + public LogicalNode visitDropDatabase(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, + DropDatabaseNode node, Stack 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 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; + +/** + * ExprAnnotator makes an annotated expression called EvalNode 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 { + 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); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Logical Operator Section + /////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public EvalNode visitAnd(Context ctx, Stack 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 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 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 stack, BinaryOperator expr) throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + @Override + public EvalNode visitNotEquals(Context ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + @Override + public EvalNode visitLessThan(Context ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + @Override + public EvalNode visitLessThanOrEquals(Context ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + @Override + public EvalNode visitGreaterThan(Context ctx, Stack stack, BinaryOperator expr) throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + @Override + public EvalNode visitGreaterThanOrEquals(Context ctx, Stack stack, BinaryOperator expr) + throws PlanningException { + return visitCommonComparison(ctx, stack, expr); + } + + public EvalNode visitCommonComparison(Context ctx, Stack 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 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 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 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 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 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 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 stack, PatternMatchPredicate expr) + throws PlanningException { + return visitPatternMatchPredicate(ctx, stack, expr); + } + + @Override + public EvalNode visitSimilarToPredicate(Context ctx, Stack stack, PatternMatchPredicate expr) + throws PlanningException { + return visitPatternMatchPredicate(ctx, stack, expr); + } + + @Override + public EvalNode visitRegexpPredicate(Context ctx, Stack stack, PatternMatchPredicate expr) + throws PlanningException { + return visitPatternMatchPredicate(ctx, stack, expr); + } + + @Override + public EvalNode visitConcatenate(Context ctx, Stack 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 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 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 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 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 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 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 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 stack, ColumnReferenceExpr expr) + throws PlanningException { + Column column = ctx.plan.resolveColumn(ctx.currentBlock, expr); + return new FieldEval(column); + } + + @Override + public EvalNode visitTargetExpr(Context ctx, Stack stack, NamedExpr expr) throws PlanningException { + throw new PlanningException("ExprAnnotator cannot take NamedExpr"); + } + + @Override + public EvalNode visitFunction(Context ctx, Stack 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 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 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 stack, DataTypeExpr expr) throws PlanningException { + return super.visitDataType(ctx, stack, expr); + } + + @Override + public EvalNode visitCastExpr(Context ctx, Stack 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 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 stack, NullLiteral expr) throws PlanningException { + return new ConstEval(NullDatum.get()); + } + + @Override + public EvalNode visitDateLiteral(Context context, Stack 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 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 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 { + + static class Context { + Set set = new HashSet(); + OpType targetType; + + Context(OpType type) { + this.targetType = type; + } + } + + public static Set finds(Expr expr, OpType type) { + Context context = new Context(type); + ExprFinder finder = new ExprFinder(); + Stack stack = new Stack(); + stack.push(expr); + try { + finder.visit(context, new Stack(), expr); + } catch (PlanningException e) { + throw new RuntimeException(e); + } + stack.pop(); + return (Set) context.set; + } + + public Object visit(Context ctx, Stack 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; + } +}